From cd202134c04e91f00ad8343c035cbbce75fc147d Mon Sep 17 00:00:00 2001 From: chengxi Date: Tue, 17 Mar 2026 19:38:18 -0400 Subject: [PATCH 01/13] refactor(go): introduce context.Context to client methods. --- bdd/go/tests/basic_messaging.go | 12 ++-- bdd/go/tests/leader_redirection.go | 14 ++-- .../tests/tcp_test/client_feature_get_all.go | 6 +- .../tcp_test/consumers_feature_create.go | 8 +++ .../tcp_test/consumers_feature_delete.go | 7 ++ .../tcp_test/consumers_feature_get_all.go | 6 +- .../tcp_test/consumers_feature_get_by_id.go | 7 +- .../tests/tcp_test/consumers_feature_join.go | 7 ++ .../tests/tcp_test/consumers_feature_leave.go | 7 ++ bdd/go/tests/tcp_test/consumers_steps.go | 11 +-- .../tests/tcp_test/messages_feature_send.go | 7 ++ bdd/go/tests/tcp_test/messages_steps.go | 2 + .../tests/tcp_test/offset_feature_delete.go | 15 +++-- .../tcp_test/offset_feature_deserialize.go | 12 ++++ .../tcp_test/partitions_feature_create.go | 6 ++ .../tcp_test/partitions_feature_delete.go | 6 ++ bdd/go/tests/tcp_test/partitions_steps.go | 4 +- bdd/go/tests/tcp_test/pat_feature_create.go | 6 +- bdd/go/tests/tcp_test/pat_feature_delete.go | 6 +- bdd/go/tests/tcp_test/pat_feature_get_all.go | 6 +- bdd/go/tests/tcp_test/pat_steps.go | 10 +-- bdd/go/tests/tcp_test/ping_feature.go | 6 +- .../tests/tcp_test/session_feature_login.go | 10 +-- .../tests/tcp_test/session_feature_logout.go | 6 +- bdd/go/tests/tcp_test/stats_feature.go | 4 +- .../tests/tcp_test/stream_feature_create.go | 12 ++-- .../tests/tcp_test/stream_feature_delete.go | 8 ++- .../tests/tcp_test/stream_feature_get_all.go | 6 +- .../tcp_test/stream_feature_get_by_id.go | 10 +-- .../tests/tcp_test/stream_feature_update.go | 12 ++-- bdd/go/tests/tcp_test/stream_steps.go | 11 +-- bdd/go/tests/tcp_test/test_helpers.go | 3 +- bdd/go/tests/tcp_test/topic_feature_create.go | 6 ++ bdd/go/tests/tcp_test/topic_feature_delete.go | 10 +-- .../tests/tcp_test/topic_feature_get_all.go | 6 +- .../tests/tcp_test/topic_feature_get_by_id.go | 8 ++- bdd/go/tests/tcp_test/topic_feature_update.go | 8 ++- bdd/go/tests/tcp_test/topic_steps.go | 8 ++- bdd/go/tests/tcp_test/users_feature_create.go | 5 ++ bdd/go/tests/tcp_test/users_feature_delete.go | 6 +- .../tests/tcp_test/users_feature_get_all.go | 6 +- .../tests/tcp_test/users_feature_get_by_id.go | 4 +- .../tests/tcp_test/users_feature_password.go | 6 +- .../tcp_test/users_feature_permissions.go | 4 ++ bdd/go/tests/tcp_test/users_feature_update.go | 4 ++ bdd/go/tests/tcp_test/users_steps.go | 17 +++-- .../send_messages_benchmark_test.go | 13 ++-- foreign/go/client/iggy_client.go | 2 +- foreign/go/client/tcp/cluster.go | 6 +- .../client/tcp/tcp_access_token_management.go | 14 ++-- .../go/client/tcp/tcp_clients_management.go | 10 +-- .../tcp/tcp_consumer_group_management.go | 26 +++---- foreign/go/client/tcp/tcp_core.go | 18 ++++- foreign/go/client/tcp/tcp_messaging.go | 8 ++- .../go/client/tcp/tcp_offset_management.go | 14 ++-- .../go/client/tcp/tcp_partition_management.go | 10 +-- .../go/client/tcp/tcp_session_management.go | 24 ++++--- .../go/client/tcp/tcp_stream_management.go | 22 +++--- foreign/go/client/tcp/tcp_topic_management.go | 20 +++--- foreign/go/client/tcp/tcp_user_management.go | 30 +++++---- foreign/go/client/tcp/tcp_utilities.go | 10 +-- foreign/go/contracts/client.go | 67 ++++++++++++------- foreign/go/internal/util/leader_aware.go | 5 +- foreign/go/samples/consumer/consumer.go | 11 +-- foreign/go/samples/producer/producer.go | 11 +-- foreign/go/tests/tls_test.go | 14 ++-- 66 files changed, 458 insertions(+), 228 deletions(-) diff --git a/bdd/go/tests/basic_messaging.go b/bdd/go/tests/basic_messaging.go index 971f5abf62..6e8fdd3bfd 100644 --- a/bdd/go/tests/basic_messaging.go +++ b/bdd/go/tests/basic_messaging.go @@ -72,11 +72,11 @@ func (s basicMessagingSteps) givenAuthenticationAsRoot(ctx context.Context) erro if err != nil { return fmt.Errorf("error creating client: %w", err) } - if err = cli.Ping(); err != nil { + if err = cli.Ping(ctx); err != nil { return fmt.Errorf("error pinging client: %w", err) } - if _, err = cli.LoginUser("iggy", "iggy"); err != nil { + if _, err = cli.LoginUser(ctx, "iggy", "iggy"); err != nil { return fmt.Errorf("error logging in: %v", err) } @@ -99,7 +99,7 @@ func (s basicMessagingSteps) whenSendMessages( streamIdentifier, _ := iggcon.NewIdentifier(streamID) topicIdentifier, _ := iggcon.NewIdentifier(topicID) partitioning := iggcon.PartitionId(partitionID) - if err = c.client.SendMessages(streamIdentifier, topicIdentifier, partitioning, messages); err != nil { + if err = c.client.SendMessages(ctx, streamIdentifier, topicIdentifier, partitioning, messages); err != nil { return fmt.Errorf("failed to sending messages: %w", err) } @@ -133,6 +133,7 @@ func (s basicMessagingSteps) whenPollMessages( topicIdentifier, _ := iggcon.NewIdentifier(topicID) uint32PartitionID := partitionID polledMessages, err := c.client.PollMessages( + ctx, streamIdentifier, topicIdentifier, consumer, @@ -211,7 +212,7 @@ func (s basicMessagingSteps) thenLastPolledMessageMatchesSent(ctx context.Contex func (s basicMessagingSteps) givenNoStreams(ctx context.Context) error { client := getBasicMessagingCtx(ctx).client - streams, err := client.GetStreams() + streams, err := client.GetStreams(ctx) if err != nil { return fmt.Errorf("failed to get streams: %w", err) } @@ -225,7 +226,7 @@ func (s basicMessagingSteps) givenNoStreams(ctx context.Context) error { func (s basicMessagingSteps) whenCreateStream(ctx context.Context, streamName string) error { c := getBasicMessagingCtx(ctx) - stream, err := c.client.CreateStream(streamName) + stream, err := c.client.CreateStream(ctx, streamName) if err != nil { return fmt.Errorf("failed to create stream: %w", err) } @@ -257,6 +258,7 @@ func (s basicMessagingSteps) whenCreateTopic(ctx context.Context, c := getBasicMessagingCtx(ctx) streamIdentifier, _ := iggcon.NewIdentifier(streamID) topic, err := c.client.CreateTopic( + ctx, streamIdentifier, topicName, partitionsCount, diff --git a/bdd/go/tests/leader_redirection.go b/bdd/go/tests/leader_redirection.go index 6832ef72dc..aac2379433 100644 --- a/bdd/go/tests/leader_redirection.go +++ b/bdd/go/tests/leader_redirection.go @@ -122,7 +122,7 @@ func serverTypeFromPort(port uint16) string { } func verifyLeaderInMetadata(client iggcon.Client) (*iggcon.ClusterNode, error) { - metadata, err := client.GetClusterMetadata() + metadata, err := client.GetClusterMetadata(context.Background()) if err != nil { if isClusteringUnavailable(err) { // Clustering not enabled, this is OK @@ -155,7 +155,7 @@ func verifyClientConnection(client iggcon.Client, expectedPort uint16) (string, } // Verify client can communicate - if err := client.Ping(); err != nil { + if err := client.Ping(context.Background()); err != nil { return "", fmt.Errorf("client cannot ping server: %w", err) } @@ -324,7 +324,7 @@ func (s leaderSteps) whenAuthenticateRoot(ctx context.Context) error { if !ok { return fmt.Errorf("client %s should be created", name) } - if _, err := cli.LoginUser(defaultRootUsername, defaultRootPassword); err != nil { + if _, err := cli.LoginUser(ctx, defaultRootUsername, defaultRootPassword); err != nil { return err } // Small delay between multiple authentications to avoid race conditions @@ -342,7 +342,7 @@ func (s leaderSteps) whenCreateStream(ctx context.Context, streamName string) er return errors.New("client should be available") } - stream, err := cli.CreateStream(streamName) + stream, err := cli.CreateStream(ctx, streamName) if err != nil { return err } @@ -437,7 +437,7 @@ func (s leaderSteps) thenConnectWithoutRedirection(ctx context.Context) error { if !ok { return errors.New("client should exist") } - if err := cli.Ping(); err != nil { + if err := cli.Ping(ctx); err != nil { return fmt.Errorf("client should be able to ping server: %v", err) } if c.TestState.RedirectionOccurred { @@ -460,10 +460,10 @@ func (s leaderSteps) thenBothUseSameServer(ctx context.Context) error { if a.GetConnectionInfo().ServerAddress != b.GetConnectionInfo().ServerAddress { return errors.New("both clients should be connected to the same server") } - if err := a.Ping(); err != nil { + if err := a.Ping(ctx); err != nil { return errors.New("client A should be able to ping") } - if err := b.Ping(); err != nil { + if err := b.Ping(ctx); err != nil { return errors.New("client B should be able to ping") } diff --git a/bdd/go/tests/tcp_test/client_feature_get_all.go b/bdd/go/tests/tcp_test/client_feature_get_all.go index e8e1f3fd1f..aed1604fae 100644 --- a/bdd/go/tests/tcp_test/client_feature_get_all.go +++ b/bdd/go/tests/tcp_test/client_feature_get_all.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" ) @@ -26,7 +28,7 @@ var _ = ginkgo.Describe("GET ALL CLIENT FEATURE:", func() { ginkgo.When("user is logged in", func() { ginkgo.Context("and tries to log with correct data", func() { client := createAuthorizedConnection() - clients, err := client.GetClients() + clients, err := client.GetClients(context.Background()) itShouldNotReturnError(err) ginkgo.It("should return stats", func() { @@ -42,7 +44,7 @@ var _ = ginkgo.Describe("GET ALL CLIENT FEATURE:", func() { ginkgo.When("user is not logged in", func() { ginkgo.Context("and tries get all clients", func() { client := createClient() - clients, err := client.GetClients() + clients, err := client.GetClients(context.Background()) itShouldReturnUnauthenticatedError(err) ginkgo.It("should not return clients", func() { diff --git a/bdd/go/tests/tcp_test/consumers_feature_create.go b/bdd/go/tests/tcp_test/consumers_feature_create.go index f9a87bd792..162e19ba3a 100644 --- a/bdd/go/tests/tcp_test/consumers_feature_create.go +++ b/bdd/go/tests/tcp_test/consumers_feature_create.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" ierror "github.com/apache/iggy/foreign/go/errors" "github.com/onsi/ginkgo/v2" @@ -35,6 +37,7 @@ var _ = ginkgo.Describe("CREATE CONSUMER GROUP:", func() { topicIdentifier, _ := iggcon.NewIdentifier(topicId) name := createRandomString(16) group, err := client.CreateConsumerGroup( + context.Background(), streamIdentifier, topicIdentifier, name, @@ -47,6 +50,7 @@ var _ = ginkgo.Describe("CREATE CONSUMER GROUP:", func() { ginkgo.Context("and tries to create consumer group for a non existing stream", func() { client := createAuthorizedConnection() _, err := client.CreateConsumerGroup( + context.Background(), randomU32Identifier(), randomU32Identifier(), createRandomString(16), @@ -61,6 +65,7 @@ var _ = ginkgo.Describe("CREATE CONSUMER GROUP:", func() { defer deleteStreamAfterTests(streamId, client) streamIdentifier, _ := iggcon.NewIdentifier(streamId) _, err := client.CreateConsumerGroup( + context.Background(), streamIdentifier, randomU32Identifier(), createRandomString(16), @@ -79,6 +84,7 @@ var _ = ginkgo.Describe("CREATE CONSUMER GROUP:", func() { streamIdentifier, _ := iggcon.NewIdentifier(streamId) topicIdentifier, _ := iggcon.NewIdentifier(topicId) _, err := client.CreateConsumerGroup( + context.Background(), streamIdentifier, topicIdentifier, name, @@ -95,6 +101,7 @@ var _ = ginkgo.Describe("CREATE CONSUMER GROUP:", func() { streamIdentifier, _ := iggcon.NewIdentifier(streamId) topicIdentifier, _ := iggcon.NewIdentifier(topicId) _, err := client.CreateConsumerGroup( + context.Background(), streamIdentifier, topicIdentifier, createRandomString(256), @@ -108,6 +115,7 @@ var _ = ginkgo.Describe("CREATE CONSUMER GROUP:", func() { ginkgo.Context("and tries to create consumer group", func() { client := createClient() _, err := client.CreateConsumerGroup( + context.Background(), randomU32Identifier(), randomU32Identifier(), createRandomString(16), diff --git a/bdd/go/tests/tcp_test/consumers_feature_delete.go b/bdd/go/tests/tcp_test/consumers_feature_delete.go index 45f3e0680d..e3a09f1e01 100644 --- a/bdd/go/tests/tcp_test/consumers_feature_delete.go +++ b/bdd/go/tests/tcp_test/consumers_feature_delete.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" ierror "github.com/apache/iggy/foreign/go/errors" "github.com/onsi/ginkgo/v2" @@ -37,6 +39,7 @@ var _ = ginkgo.Describe("DELETE CONSUMER GROUP:", func() { topicIdentifier, _ := iggcon.NewIdentifier(topicId) groupIdentifier, _ := iggcon.NewIdentifier(groupId) err := client.DeleteConsumerGroup( + context.Background(), streamIdentifier, topicIdentifier, groupIdentifier, @@ -55,6 +58,7 @@ var _ = ginkgo.Describe("DELETE CONSUMER GROUP:", func() { streamIdentifier, _ := iggcon.NewIdentifier(streamId) topicIdentifier, _ := iggcon.NewIdentifier(topicId) err := client.DeleteConsumerGroup( + context.Background(), streamIdentifier, topicIdentifier, randomU32Identifier(), @@ -70,6 +74,7 @@ var _ = ginkgo.Describe("DELETE CONSUMER GROUP:", func() { streamIdentifier, _ := iggcon.NewIdentifier(streamId) err := client.DeleteConsumerGroup( + context.Background(), streamIdentifier, randomU32Identifier(), randomU32Identifier(), @@ -81,6 +86,7 @@ var _ = ginkgo.Describe("DELETE CONSUMER GROUP:", func() { ginkgo.Context("and tries to delete consumer for non-existing topic and stream", func() { client := createAuthorizedConnection() err := client.DeleteConsumerGroup( + context.Background(), randomU32Identifier(), randomU32Identifier(), randomU32Identifier(), @@ -94,6 +100,7 @@ var _ = ginkgo.Describe("DELETE CONSUMER GROUP:", func() { ginkgo.Context("and tries to delete consumer group", func() { client := createClient() err := client.DeleteConsumerGroup( + context.Background(), randomU32Identifier(), randomU32Identifier(), randomU32Identifier(), diff --git a/bdd/go/tests/tcp_test/consumers_feature_get_all.go b/bdd/go/tests/tcp_test/consumers_feature_get_all.go index d4546fce66..493e58c633 100644 --- a/bdd/go/tests/tcp_test/consumers_feature_get_all.go +++ b/bdd/go/tests/tcp_test/consumers_feature_get_all.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" "github.com/onsi/ginkgo/v2" ) @@ -33,7 +35,7 @@ var _ = ginkgo.Describe("GET ALL CONSUMER GROUPS:", func() { groupId, name := successfullyCreateConsumer(streamId, topicId, client) streamIdentifier, _ := iggcon.NewIdentifier(streamId) topicIdentifier, _ := iggcon.NewIdentifier(topicId) - groups, err := client.GetConsumerGroups(streamIdentifier, topicIdentifier) + groups, err := client.GetConsumerGroups(context.Background(), streamIdentifier, topicIdentifier) itShouldNotReturnError(err) itShouldContainSpecificConsumer(groupId, name, groups) @@ -43,7 +45,7 @@ var _ = ginkgo.Describe("GET ALL CONSUMER GROUPS:", func() { ginkgo.When("User is not logged in", func() { ginkgo.Context("and tries to get all consumer groups", func() { client := createClient() - _, err := client.GetConsumerGroups(randomU32Identifier(), randomU32Identifier()) + _, err := client.GetConsumerGroups(context.Background(), randomU32Identifier(), randomU32Identifier()) itShouldReturnUnauthenticatedError(err) }) diff --git a/bdd/go/tests/tcp_test/consumers_feature_get_by_id.go b/bdd/go/tests/tcp_test/consumers_feature_get_by_id.go index bc6c4dce31..49d3f3b7ce 100644 --- a/bdd/go/tests/tcp_test/consumers_feature_get_by_id.go +++ b/bdd/go/tests/tcp_test/consumers_feature_get_by_id.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" ierror "github.com/apache/iggy/foreign/go/errors" "github.com/onsi/ginkgo/v2" @@ -35,7 +37,7 @@ var _ = ginkgo.Describe("GET CONSUMER GROUP BY ID:", func() { streamIdentifier, _ := iggcon.NewIdentifier(streamId) topicIdentifier, _ := iggcon.NewIdentifier(topicId) groupIdentifier, _ := iggcon.NewIdentifier(groupId) - group, err := client.GetConsumerGroup(streamIdentifier, topicIdentifier, groupIdentifier) + group, err := client.GetConsumerGroup(context.Background(), streamIdentifier, topicIdentifier, groupIdentifier) itShouldNotReturnError(err) itShouldReturnSpecificConsumer(groupId, name, &group.ConsumerGroup) @@ -45,6 +47,7 @@ var _ = ginkgo.Describe("GET CONSUMER GROUP BY ID:", func() { client := createAuthorizedConnection() _, err := client.GetConsumerGroup( + context.Background(), randomU32Identifier(), randomU32Identifier(), randomU32Identifier(), @@ -59,6 +62,7 @@ var _ = ginkgo.Describe("GET CONSUMER GROUP BY ID:", func() { defer deleteStreamAfterTests(streamId, client) streamIdentifier, _ := iggcon.NewIdentifier(streamId) _, err := client.GetConsumerGroup( + context.Background(), streamIdentifier, randomU32Identifier(), randomU32Identifier(), @@ -75,6 +79,7 @@ var _ = ginkgo.Describe("GET CONSUMER GROUP BY ID:", func() { streamIdentifier, _ := iggcon.NewIdentifier(streamId) topicIdentifier, _ := iggcon.NewIdentifier(topicId) _, err := client.GetConsumerGroup( + context.Background(), streamIdentifier, topicIdentifier, randomU32Identifier(), diff --git a/bdd/go/tests/tcp_test/consumers_feature_join.go b/bdd/go/tests/tcp_test/consumers_feature_join.go index 9342f3ad61..00ad8f22d3 100644 --- a/bdd/go/tests/tcp_test/consumers_feature_join.go +++ b/bdd/go/tests/tcp_test/consumers_feature_join.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" ierror "github.com/apache/iggy/foreign/go/errors" "github.com/onsi/ginkgo/v2" @@ -36,6 +38,7 @@ var _ = ginkgo.Describe("JOIN CONSUMER GROUP:", func() { topicIdentifier, _ := iggcon.NewIdentifier(topicId) groupIdentifier, _ := iggcon.NewIdentifier(groupId) err := client.JoinConsumerGroup( + context.Background(), streamIdentifier, topicIdentifier, groupIdentifier, @@ -53,6 +56,7 @@ var _ = ginkgo.Describe("JOIN CONSUMER GROUP:", func() { streamIdentifier, _ := iggcon.NewIdentifier(streamId) topicIdentifier, _ := iggcon.NewIdentifier(topicId) err := client.JoinConsumerGroup( + context.Background(), streamIdentifier, topicIdentifier, randomU32Identifier(), @@ -67,6 +71,7 @@ var _ = ginkgo.Describe("JOIN CONSUMER GROUP:", func() { defer deleteStreamAfterTests(streamId, client) streamIdentifier, _ := iggcon.NewIdentifier(streamId) err := client.JoinConsumerGroup( + context.Background(), streamIdentifier, randomU32Identifier(), randomU32Identifier(), @@ -78,6 +83,7 @@ var _ = ginkgo.Describe("JOIN CONSUMER GROUP:", func() { ginkgo.Context("and tries to join consumer for non-existing topic and stream", func() { client := createAuthorizedConnection() err := client.JoinConsumerGroup( + context.Background(), randomU32Identifier(), randomU32Identifier(), randomU32Identifier(), @@ -91,6 +97,7 @@ var _ = ginkgo.Describe("JOIN CONSUMER GROUP:", func() { ginkgo.Context("and tries to join to the consumer group", func() { client := createClient() err := client.JoinConsumerGroup( + context.Background(), randomU32Identifier(), randomU32Identifier(), randomU32Identifier(), diff --git a/bdd/go/tests/tcp_test/consumers_feature_leave.go b/bdd/go/tests/tcp_test/consumers_feature_leave.go index 787f5e173e..0a9997da10 100644 --- a/bdd/go/tests/tcp_test/consumers_feature_leave.go +++ b/bdd/go/tests/tcp_test/consumers_feature_leave.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" ierror "github.com/apache/iggy/foreign/go/errors" "github.com/onsi/ginkgo/v2" @@ -38,6 +40,7 @@ var _ = ginkgo.Describe("LEAVE CONSUMER GROUP:", func() { topicIdentifier, _ := iggcon.NewIdentifier(topicId) groupIdentifier, _ := iggcon.NewIdentifier(groupId) err := client.LeaveConsumerGroup( + context.Background(), streamIdentifier, topicIdentifier, groupIdentifier, @@ -55,6 +58,7 @@ var _ = ginkgo.Describe("LEAVE CONSUMER GROUP:", func() { streamIdentifier, _ := iggcon.NewIdentifier(streamId) topicIdentifier, _ := iggcon.NewIdentifier(topicId) err := client.LeaveConsumerGroup( + context.Background(), streamIdentifier, topicIdentifier, randomU32Identifier(), @@ -69,6 +73,7 @@ var _ = ginkgo.Describe("LEAVE CONSUMER GROUP:", func() { defer deleteStreamAfterTests(streamId, client) streamIdentifier, _ := iggcon.NewIdentifier(streamId) err := client.LeaveConsumerGroup( + context.Background(), streamIdentifier, randomU32Identifier(), randomU32Identifier(), @@ -81,6 +86,7 @@ var _ = ginkgo.Describe("LEAVE CONSUMER GROUP:", func() { client := createAuthorizedConnection() err := client.LeaveConsumerGroup( + context.Background(), randomU32Identifier(), randomU32Identifier(), randomU32Identifier(), @@ -94,6 +100,7 @@ var _ = ginkgo.Describe("LEAVE CONSUMER GROUP:", func() { ginkgo.Context("and tries to leave to the consumer group", func() { client := createClient() err := client.LeaveConsumerGroup( + context.Background(), randomU32Identifier(), randomU32Identifier(), randomU32Identifier(), diff --git a/bdd/go/tests/tcp_test/consumers_steps.go b/bdd/go/tests/tcp_test/consumers_steps.go index 3c56ab3bae..f00d885be9 100644 --- a/bdd/go/tests/tcp_test/consumers_steps.go +++ b/bdd/go/tests/tcp_test/consumers_steps.go @@ -18,6 +18,7 @@ package tcp_test import ( + "context" "fmt" iggcon "github.com/apache/iggy/foreign/go/contracts" @@ -32,6 +33,7 @@ func successfullyCreateConsumer(streamId uint32, topicId uint32, cli iggcon.Clie streamIdentifier, _ := iggcon.NewIdentifier(streamId) topicIdentifier, _ := iggcon.NewIdentifier(topicId) group, err := cli.CreateConsumerGroup( + context.Background(), streamIdentifier, topicIdentifier, name) @@ -46,6 +48,7 @@ func successfullyJoinConsumer(streamId uint32, topicId uint32, groupId uint32, c topicIdentifier, _ := iggcon.NewIdentifier(topicId) groupIdentifier, _ := iggcon.NewIdentifier(groupId) err := client.JoinConsumerGroup( + context.Background(), streamIdentifier, topicIdentifier, groupIdentifier, @@ -100,7 +103,7 @@ func itShouldSuccessfullyCreateConsumer(streamId uint32, topicId uint32, groupId streamIdentifier, _ := iggcon.NewIdentifier(streamId) topicIdentifier, _ := iggcon.NewIdentifier(topicId) groupIdentifier, _ := iggcon.NewIdentifier(groupId) - consumer, err := client.GetConsumerGroup(streamIdentifier, topicIdentifier, groupIdentifier) + consumer, err := client.GetConsumerGroup(context.Background(), streamIdentifier, topicIdentifier, groupIdentifier) ginkgo.It("should create consumer with id "+string(rune(groupId)), func() { gomega.Expect(consumer).NotTo(gomega.BeNil()) gomega.Expect(consumer.Id).To(gomega.Equal(groupId)) @@ -117,7 +120,7 @@ func itShouldSuccessfullyDeletedConsumer(streamId uint32, topicId uint32, groupI streamIdentifier, _ := iggcon.NewIdentifier(streamId) topicIdentifier, _ := iggcon.NewIdentifier(topicId) groupIdentifier, _ := iggcon.NewIdentifier(groupId) - consumer, err := client.GetConsumerGroup(streamIdentifier, topicIdentifier, groupIdentifier) + consumer, err := client.GetConsumerGroup(context.Background(), streamIdentifier, topicIdentifier, groupIdentifier) itShouldReturnSpecificError(err, ierror.ErrConsumerGroupIdNotFound) ginkgo.It("should not return consumer", func() { gomega.Expect(consumer).To(gomega.BeNil()) @@ -128,7 +131,7 @@ func itShouldSuccessfullyJoinConsumer(streamId uint32, topicId uint32, groupId u streamIdentifier, _ := iggcon.NewIdentifier(streamId) topicIdentifier, _ := iggcon.NewIdentifier(topicId) groupIdentifier, _ := iggcon.NewIdentifier(groupId) - consumer, err := client.GetConsumerGroup(streamIdentifier, topicIdentifier, groupIdentifier) + consumer, err := client.GetConsumerGroup(context.Background(), streamIdentifier, topicIdentifier, groupIdentifier) ginkgo.It("should join consumer with id "+string(rune(groupId)), func() { gomega.Expect(consumer).NotTo(gomega.BeNil()) @@ -148,7 +151,7 @@ func itShouldSuccessfullyLeaveConsumer(streamId uint32, topicId uint32, groupId streamIdentifier, _ := iggcon.NewIdentifier(streamId) topicIdentifier, _ := iggcon.NewIdentifier(topicId) groupIdentifier, _ := iggcon.NewIdentifier(groupId) - consumer, err := client.GetConsumerGroup(streamIdentifier, topicIdentifier, groupIdentifier) + consumer, err := client.GetConsumerGroup(context.Background(), streamIdentifier, topicIdentifier, groupIdentifier) ginkgo.It("should leave consumer with id "+string(rune(groupId)), func() { gomega.Expect(consumer).NotTo(gomega.BeNil()) gomega.Expect(consumer.MembersCount).To(gomega.Equal(uint32(0))) diff --git a/bdd/go/tests/tcp_test/messages_feature_send.go b/bdd/go/tests/tcp_test/messages_feature_send.go index b2c0b21866..9cc205b8a7 100644 --- a/bdd/go/tests/tcp_test/messages_feature_send.go +++ b/bdd/go/tests/tcp_test/messages_feature_send.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" ierror "github.com/apache/iggy/foreign/go/errors" "github.com/onsi/ginkgo/v2" @@ -35,6 +37,7 @@ var _ = ginkgo.Describe("SEND MESSAGES:", func() { streamIdentifier, _ := iggcon.NewIdentifier(streamId) topicIdentifier, _ := iggcon.NewIdentifier(topicId) err := client.SendMessages( + context.Background(), streamIdentifier, topicIdentifier, iggcon.None(), @@ -51,6 +54,7 @@ var _ = ginkgo.Describe("SEND MESSAGES:", func() { messages := createDefaultMessages() streamIdentifier, _ := iggcon.NewIdentifier(streamId) err := client.SendMessages( + context.Background(), streamIdentifier, randomU32Identifier(), iggcon.None(), @@ -63,6 +67,7 @@ var _ = ginkgo.Describe("SEND MESSAGES:", func() { client := createAuthorizedConnection() messages := createDefaultMessages() err := client.SendMessages( + context.Background(), randomU32Identifier(), randomU32Identifier(), iggcon.None(), @@ -80,6 +85,7 @@ var _ = ginkgo.Describe("SEND MESSAGES:", func() { streamIdentifier, _ := iggcon.NewIdentifier(streamId) topicIdentifier, _ := iggcon.NewIdentifier(topicId) err := client.SendMessages( + context.Background(), streamIdentifier, topicIdentifier, iggcon.PartitionId(createRandomUInt32()), @@ -96,6 +102,7 @@ var _ = ginkgo.Describe("SEND MESSAGES:", func() { streamIdentifier, _ := iggcon.NewIdentifier(streamId) topicIdentifier, _ := iggcon.NewIdentifier(topicId) err := client.SendMessages( + context.Background(), streamIdentifier, topicIdentifier, iggcon.PartitionId(createRandomUInt32()), diff --git a/bdd/go/tests/tcp_test/messages_steps.go b/bdd/go/tests/tcp_test/messages_steps.go index 41723a1a06..6908b1b14b 100644 --- a/bdd/go/tests/tcp_test/messages_steps.go +++ b/bdd/go/tests/tcp_test/messages_steps.go @@ -19,6 +19,7 @@ package tcp_test import ( "bytes" + "context" "reflect" iggcon "github.com/apache/iggy/foreign/go/contracts" @@ -45,6 +46,7 @@ func itShouldSuccessfullyPublishMessages(streamId uint32, topicId uint32, messag streamIdentifier, _ := iggcon.NewIdentifier(streamId) topicIdentifier, _ := iggcon.NewIdentifier(topicId) result, err := client.PollMessages( + context.Background(), streamIdentifier, topicIdentifier, iggcon.NewSingleConsumer(randomU32Identifier()), diff --git a/bdd/go/tests/tcp_test/offset_feature_delete.go b/bdd/go/tests/tcp_test/offset_feature_delete.go index 70cd371150..3972d8f312 100644 --- a/bdd/go/tests/tcp_test/offset_feature_delete.go +++ b/bdd/go/tests/tcp_test/offset_feature_delete.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" ierror "github.com/apache/iggy/foreign/go/errors" "github.com/onsi/ginkgo/v2" @@ -44,6 +46,7 @@ var _ = ginkgo.Describe("DELETE CONSUMER OFFSET:", func() { // send test messages messages := createDefaultMessages() err := client.SendMessages( + context.Background(), streamIdentifier, topicIdentifier, iggcon.PartitionId(partitionId), @@ -53,6 +56,7 @@ var _ = ginkgo.Describe("DELETE CONSUMER OFFSET:", func() { // store consumer offset err = client.StoreConsumerOffset( + context.Background(), consumer, streamIdentifier, topicIdentifier, @@ -63,20 +67,20 @@ var _ = ginkgo.Describe("DELETE CONSUMER OFFSET:", func() { // verify offset was stored storedOffset, err := client.GetConsumerOffset( - consumer, streamIdentifier, topicIdentifier, &partitionId, + context.Background(), consumer, streamIdentifier, topicIdentifier, &partitionId, ) itShouldNotReturnError(err) itShouldReturnStoredConsumerOffset(storedOffset, partitionId, testOffset) // delete the offset err = client.DeleteConsumerOffset( - consumer, streamIdentifier, topicIdentifier, &partitionId, + context.Background(), consumer, streamIdentifier, topicIdentifier, &partitionId, ) itShouldNotReturnError(err) // verify offset was deleted storedOffset, err = client.GetConsumerOffset( - consumer, streamIdentifier, topicIdentifier, &partitionId, + context.Background(), consumer, streamIdentifier, topicIdentifier, &partitionId, ) itShouldNotReturnError(err) itShouldReturnNilOffsetForNewConsumerGroup(storedOffset) @@ -97,6 +101,7 @@ var _ = ginkgo.Describe("DELETE CONSUMER OFFSET:", func() { consumer := iggcon.NewSingleConsumer(groupIdentifier) err := client.DeleteConsumerOffset( + context.Background(), consumer, streamIdentifier, topicIdentifier, @@ -117,6 +122,7 @@ var _ = ginkgo.Describe("DELETE CONSUMER OFFSET:", func() { partitionId := uint32(1) err := client.DeleteConsumerOffset( + context.Background(), consumer, streamIdentifier, topicIdentifier, @@ -132,6 +138,7 @@ var _ = ginkgo.Describe("DELETE CONSUMER OFFSET:", func() { partitionId := uint32(1) err := client.DeleteConsumerOffset( + context.Background(), consumer, randomU32Identifier(), randomU32Identifier(), @@ -147,7 +154,7 @@ var _ = ginkgo.Describe("DELETE CONSUMER OFFSET:", func() { partitionId := uint32(1) err := client.DeleteConsumerOffset( - consumer, randomU32Identifier(), randomU32Identifier(), &partitionId, + context.Background(), consumer, randomU32Identifier(), randomU32Identifier(), &partitionId, ) itShouldReturnUnauthenticatedError(err) }) diff --git a/bdd/go/tests/tcp_test/offset_feature_deserialize.go b/bdd/go/tests/tcp_test/offset_feature_deserialize.go index d37808eb96..fccb0725d7 100644 --- a/bdd/go/tests/tcp_test/offset_feature_deserialize.go +++ b/bdd/go/tests/tcp_test/offset_feature_deserialize.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" @@ -39,6 +41,7 @@ var _ = ginkgo.Describe("GET CONSUMER OFFSET:", func() { groupIdentifier, _ := iggcon.NewIdentifier(groupId) joinErr := client.JoinConsumerGroup( + context.Background(), streamIdentifier, topicIdentifier, groupIdentifier, @@ -48,6 +51,7 @@ var _ = ginkgo.Describe("GET CONSUMER OFFSET:", func() { partitionId := uint32(1) offset, err := client.GetConsumerOffset( + context.Background(), consumer, streamIdentifier, topicIdentifier, @@ -77,6 +81,7 @@ var _ = ginkgo.Describe("GET CONSUMER OFFSET:", func() { messages := createDefaultMessages() sendErr := client.SendMessages( + context.Background(), streamIdentifier, topicIdentifier, iggcon.PartitionId(partitionId), @@ -84,6 +89,7 @@ var _ = ginkgo.Describe("GET CONSUMER OFFSET:", func() { ) storeErr := client.StoreConsumerOffset( + context.Background(), consumer, streamIdentifier, topicIdentifier, @@ -92,6 +98,7 @@ var _ = ginkgo.Describe("GET CONSUMER OFFSET:", func() { ) offset, getErr := client.GetConsumerOffset( + context.Background(), consumer, streamIdentifier, topicIdentifier, @@ -116,6 +123,7 @@ var _ = ginkgo.Describe("GET CONSUMER OFFSET:", func() { groupIdentifier, _ := iggcon.NewIdentifier(groupId) joinErr := client.JoinConsumerGroup( + context.Background(), streamIdentifier, topicIdentifier, groupIdentifier, @@ -127,6 +135,7 @@ var _ = ginkgo.Describe("GET CONSUMER OFFSET:", func() { // Don't store any offset - we want to test that a new consumer group has no stored offset storedOffset, getErr := client.GetConsumerOffset( + context.Background(), consumer, streamIdentifier, topicIdentifier, @@ -150,6 +159,7 @@ var _ = ginkgo.Describe("GET CONSUMER OFFSET:", func() { partitionId := uint32(1) offset, err := client.GetConsumerOffset( + context.Background(), consumer, streamIdentifier, topicIdentifier, @@ -166,6 +176,7 @@ var _ = ginkgo.Describe("GET CONSUMER OFFSET:", func() { partitionId := uint32(1) offset, err := client.GetConsumerOffset( + context.Background(), consumer, randomU32Identifier(), randomU32Identifier(), @@ -184,6 +195,7 @@ var _ = ginkgo.Describe("GET CONSUMER OFFSET:", func() { partitionId := uint32(1) _, err := client.GetConsumerOffset( + context.Background(), consumer, randomU32Identifier(), randomU32Identifier(), diff --git a/bdd/go/tests/tcp_test/partitions_feature_create.go b/bdd/go/tests/tcp_test/partitions_feature_create.go index 432c8462c8..d9890570d9 100644 --- a/bdd/go/tests/tcp_test/partitions_feature_create.go +++ b/bdd/go/tests/tcp_test/partitions_feature_create.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" ierror "github.com/apache/iggy/foreign/go/errors" "github.com/onsi/ginkgo/v2" @@ -35,6 +37,7 @@ var _ = ginkgo.Describe("CREATE PARTITION:", func() { topicIdentifier, _ := iggcon.NewIdentifier(topicId) partitionsCount := uint32(10) err := client.CreatePartitions( + context.Background(), streamIdentifier, topicIdentifier, partitionsCount, @@ -47,6 +50,7 @@ var _ = ginkgo.Describe("CREATE PARTITION:", func() { ginkgo.Context("and tries to create partitions for a non existing stream", func() { client := createAuthorizedConnection() err := client.CreatePartitions( + context.Background(), randomU32Identifier(), randomU32Identifier(), 10, @@ -61,6 +65,7 @@ var _ = ginkgo.Describe("CREATE PARTITION:", func() { defer deleteStreamAfterTests(streamId, client) streamIdentifier, _ := iggcon.NewIdentifier(streamId) err := client.CreatePartitions( + context.Background(), streamIdentifier, randomU32Identifier(), 10, @@ -74,6 +79,7 @@ var _ = ginkgo.Describe("CREATE PARTITION:", func() { ginkgo.Context("and tries to create partitions", func() { client := createClient() err := client.CreatePartitions( + context.Background(), randomU32Identifier(), randomU32Identifier(), 10, diff --git a/bdd/go/tests/tcp_test/partitions_feature_delete.go b/bdd/go/tests/tcp_test/partitions_feature_delete.go index ca759ffabb..8816e79ffc 100644 --- a/bdd/go/tests/tcp_test/partitions_feature_delete.go +++ b/bdd/go/tests/tcp_test/partitions_feature_delete.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" ierror "github.com/apache/iggy/foreign/go/errors" "github.com/onsi/ginkgo/v2" @@ -35,6 +37,7 @@ var _ = ginkgo.Describe("DELETE PARTITION:", func() { topicIdentifier, _ := iggcon.NewIdentifier(topicId) partitionsCount := uint32(1) err := client.DeletePartitions( + context.Background(), streamIdentifier, topicIdentifier, 1, @@ -47,6 +50,7 @@ var _ = ginkgo.Describe("DELETE PARTITION:", func() { ginkgo.Context("and tries to delete partitions for a non existing stream", func() { client := createAuthorizedConnection() err := client.DeletePartitions( + context.Background(), randomU32Identifier(), randomU32Identifier(), 10, @@ -61,6 +65,7 @@ var _ = ginkgo.Describe("DELETE PARTITION:", func() { defer deleteStreamAfterTests(streamId, client) streamIdentifier, _ := iggcon.NewIdentifier(streamId) err := client.DeletePartitions( + context.Background(), streamIdentifier, randomU32Identifier(), 10, @@ -74,6 +79,7 @@ var _ = ginkgo.Describe("DELETE PARTITION:", func() { ginkgo.Context("and tries to delete partitions", func() { client := createClient() err := client.DeletePartitions( + context.Background(), randomU32Identifier(), randomU32Identifier(), 10, diff --git a/bdd/go/tests/tcp_test/partitions_steps.go b/bdd/go/tests/tcp_test/partitions_steps.go index 703e34cb52..251f0ecc9a 100644 --- a/bdd/go/tests/tcp_test/partitions_steps.go +++ b/bdd/go/tests/tcp_test/partitions_steps.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" @@ -26,7 +28,7 @@ import ( func itShouldHaveExpectedNumberOfPartitions(streamId uint32, topicId uint32, expectedPartitions uint32, client iggcon.Client) { streamIdentifier, _ := iggcon.NewIdentifier(streamId) topicIdentifier, _ := iggcon.NewIdentifier(topicId) - topic, err := client.GetTopic(streamIdentifier, topicIdentifier) + topic, err := client.GetTopic(context.Background(), streamIdentifier, topicIdentifier) ginkgo.It("should have "+string(rune(expectedPartitions))+" partitions", func() { gomega.Expect(topic).NotTo(gomega.BeNil()) diff --git a/bdd/go/tests/tcp_test/pat_feature_create.go b/bdd/go/tests/tcp_test/pat_feature_create.go index bcce5c4c25..9619bde7fc 100644 --- a/bdd/go/tests/tcp_test/pat_feature_create.go +++ b/bdd/go/tests/tcp_test/pat_feature_create.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + "github.com/onsi/ginkgo/v2" ) @@ -26,7 +28,7 @@ var _ = ginkgo.Describe("CREATE PAT:", func() { ginkgo.Context("tries to create PAT with correct data", func() { client := createAuthorizedConnection() name := createRandomString(16) - response, err := client.CreatePersonalAccessToken(name, 0) + response, err := client.CreatePersonalAccessToken(context.Background(), name, 0) itShouldNotReturnError(err) itShouldSuccessfullyCreateAccessToken(name, client) @@ -37,7 +39,7 @@ var _ = ginkgo.Describe("CREATE PAT:", func() { ginkgo.When("User is not logged in", func() { ginkgo.Context("and tries to create PAT", func() { client := createClient() - _, err := client.CreatePersonalAccessToken(createRandomString(16), 0) + _, err := client.CreatePersonalAccessToken(context.Background(), createRandomString(16), 0) itShouldReturnUnauthenticatedError(err) }) }) diff --git a/bdd/go/tests/tcp_test/pat_feature_delete.go b/bdd/go/tests/tcp_test/pat_feature_delete.go index c9bec6722a..b1afe4a8a1 100644 --- a/bdd/go/tests/tcp_test/pat_feature_delete.go +++ b/bdd/go/tests/tcp_test/pat_feature_delete.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + "github.com/onsi/ginkgo/v2" ) @@ -28,7 +30,7 @@ var _ = ginkgo.Describe("DELETE PAT:", func() { name := createRandomString(16) token := successfullyCreateAccessToken(name, client) - err := client.DeletePersonalAccessToken(name) + err := client.DeletePersonalAccessToken(context.Background(), name) itShouldNotReturnError(err) itShouldSuccessfullyDeleteAccessToken(token, client) @@ -38,7 +40,7 @@ var _ = ginkgo.Describe("DELETE PAT:", func() { ginkgo.When("User is not logged in", func() { ginkgo.Context("and tries to delete PAT", func() { client := createClient() - err := client.DeletePersonalAccessToken(createRandomString(16)) + err := client.DeletePersonalAccessToken(context.Background(), createRandomString(16)) itShouldReturnUnauthenticatedError(err) }) }) diff --git a/bdd/go/tests/tcp_test/pat_feature_get_all.go b/bdd/go/tests/tcp_test/pat_feature_get_all.go index 2eb396969a..3ae60502a8 100644 --- a/bdd/go/tests/tcp_test/pat_feature_get_all.go +++ b/bdd/go/tests/tcp_test/pat_feature_get_all.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + "github.com/onsi/ginkgo/v2" ) @@ -28,7 +30,7 @@ var _ = ginkgo.Describe("GET PAT:", func() { name := createRandomString(16) successfullyCreateAccessToken(name, client) - tokens, err := client.GetPersonalAccessTokens() + tokens, err := client.GetPersonalAccessTokens(context.Background()) itShouldNotReturnError(err) itShouldContainSpecificAccessToken(name, tokens) @@ -38,7 +40,7 @@ var _ = ginkgo.Describe("GET PAT:", func() { ginkgo.When("User is not logged in", func() { ginkgo.Context("and tries to all get PAT's", func() { client := createClient() - _, err := client.GetPersonalAccessTokens() + _, err := client.GetPersonalAccessTokens(context.Background()) itShouldReturnUnauthenticatedError(err) }) }) diff --git a/bdd/go/tests/tcp_test/pat_steps.go b/bdd/go/tests/tcp_test/pat_steps.go index aa262ba9ea..e5a1da01ac 100644 --- a/bdd/go/tests/tcp_test/pat_steps.go +++ b/bdd/go/tests/tcp_test/pat_steps.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" @@ -26,7 +28,7 @@ import ( // OPERATIONS func successfullyCreateAccessToken(name string, client iggcon.Client) string { - result, err := client.CreatePersonalAccessToken(name, 0) + result, err := client.CreatePersonalAccessToken(context.Background(), name, 0) itShouldNotReturnError(err) return result.Token @@ -35,14 +37,14 @@ func successfullyCreateAccessToken(name string, client iggcon.Client) string { // ASSERTIONS func itShouldSuccessfullyCreateAccessToken(name string, client iggcon.Client) { - tokens, err := client.GetPersonalAccessTokens() + tokens, err := client.GetPersonalAccessTokens(context.Background()) itShouldNotReturnError(err) itShouldContainSpecificAccessToken(name, tokens) } func itShouldSuccessfullyDeleteAccessToken(name string, client iggcon.Client) { - tokens, err := client.GetPersonalAccessTokens() + tokens, err := client.GetPersonalAccessTokens(context.Background()) itShouldNotReturnError(err) found := false @@ -60,7 +62,7 @@ func itShouldSuccessfullyDeleteAccessToken(name string, client iggcon.Client) { func itShouldBePossibleToLogInWithAccessToken(token string) { ms := createClient() - userId, err := ms.LoginWithPersonalAccessToken(token) + userId, err := ms.LoginWithPersonalAccessToken(context.Background(), token) itShouldNotReturnError(err) ginkgo.It("should return userId", func() { diff --git a/bdd/go/tests/tcp_test/ping_feature.go b/bdd/go/tests/tcp_test/ping_feature.go index 314c9a68fe..5d840b5103 100644 --- a/bdd/go/tests/tcp_test/ping_feature.go +++ b/bdd/go/tests/tcp_test/ping_feature.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + "github.com/onsi/ginkgo/v2" ) @@ -25,7 +27,7 @@ var _ = ginkgo.Describe("PING FEATURE:", func() { ginkgo.When("User is logged in", func() { ginkgo.Context("and tries to ping server", func() { client := createAuthorizedConnection() - err := client.Ping() + err := client.Ping(context.Background()) itShouldNotReturnError(err) }) @@ -34,7 +36,7 @@ var _ = ginkgo.Describe("PING FEATURE:", func() { ginkgo.When("User is not logged in", func() { ginkgo.Context("and tries to ping server", func() { client := createClient() - err := client.Ping() + err := client.Ping(context.Background()) itShouldNotReturnError(err) }) diff --git a/bdd/go/tests/tcp_test/session_feature_login.go b/bdd/go/tests/tcp_test/session_feature_login.go index 051f7e5581..ccb1b08e51 100644 --- a/bdd/go/tests/tcp_test/session_feature_login.go +++ b/bdd/go/tests/tcp_test/session_feature_login.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" @@ -27,7 +29,7 @@ var _ = ginkgo.Describe("LOGIN FEATURE:", func() { ginkgo.When("user is already logged in", func() { ginkgo.Context("and tries to log with correct data", func() { client := createAuthorizedConnection() - user, err := client.LoginUser("iggy", "iggy") + user, err := client.LoginUser(context.Background(), "iggy", "iggy") itShouldNotReturnError(err) itShouldReturnUserId(user, 0) @@ -35,7 +37,7 @@ var _ = ginkgo.Describe("LOGIN FEATURE:", func() { ginkgo.Context("and tries to log with invalid credentials", func() { client := createAuthorizedConnection() - user, err := client.LoginUser("incorrect", "random") + user, err := client.LoginUser(context.Background(), "incorrect", "random") itShouldReturnError(err) itShouldNotReturnUser(user) @@ -45,7 +47,7 @@ var _ = ginkgo.Describe("LOGIN FEATURE:", func() { ginkgo.When("user is not logged in", func() { ginkgo.Context("and tries to log with correct data", func() { client := createClient() - user, err := client.LoginUser("iggy", "iggy") + user, err := client.LoginUser(context.Background(), "iggy", "iggy") itShouldNotReturnError(err) itShouldReturnUserId(user, 0) @@ -53,7 +55,7 @@ var _ = ginkgo.Describe("LOGIN FEATURE:", func() { ginkgo.Context("and tries to log with invalid credentials", func() { client := createClient() - user, err := client.LoginUser("incorrect", "random") + user, err := client.LoginUser(context.Background(), "incorrect", "random") itShouldReturnError(err) itShouldNotReturnUser(user) diff --git a/bdd/go/tests/tcp_test/session_feature_logout.go b/bdd/go/tests/tcp_test/session_feature_logout.go index afc91f57c9..fddab18192 100644 --- a/bdd/go/tests/tcp_test/session_feature_logout.go +++ b/bdd/go/tests/tcp_test/session_feature_logout.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + "github.com/onsi/ginkgo/v2" ) @@ -25,7 +27,7 @@ var _ = ginkgo.Describe("LOGOUT FEATURE:", func() { ginkgo.When("User is logged in", func() { ginkgo.Context("and tries to log out", func() { client := createAuthorizedConnection() - err := client.LogoutUser() + err := client.LogoutUser(context.Background()) itShouldNotReturnError(err) }) @@ -34,7 +36,7 @@ var _ = ginkgo.Describe("LOGOUT FEATURE:", func() { ginkgo.When("User is not logged in", func() { ginkgo.Context("and tries to log out", func() { client := createClient() - err := client.LogoutUser() + err := client.LogoutUser(context.Background()) itShouldReturnUnauthenticatedError(err) }) diff --git a/bdd/go/tests/tcp_test/stats_feature.go b/bdd/go/tests/tcp_test/stats_feature.go index 769403eb3a..5d5e34064d 100644 --- a/bdd/go/tests/tcp_test/stats_feature.go +++ b/bdd/go/tests/tcp_test/stats_feature.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" ) @@ -26,7 +28,7 @@ var _ = ginkgo.Describe("STAT FEATURE:", func() { ginkgo.When("user is logged in", func() { ginkgo.Context("and tries to log with correct data", func() { client := createAuthorizedConnection() - stats, err := client.GetStats() + stats, err := client.GetStats(context.Background()) itShouldNotReturnError(err) ginkgo.It("should return stats", func() { diff --git a/bdd/go/tests/tcp_test/stream_feature_create.go b/bdd/go/tests/tcp_test/stream_feature_create.go index 92fc5e25e8..5502c7967f 100644 --- a/bdd/go/tests/tcp_test/stream_feature_create.go +++ b/bdd/go/tests/tcp_test/stream_feature_create.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + ierror "github.com/apache/iggy/foreign/go/errors" "github.com/onsi/ginkgo/v2" ) @@ -28,7 +30,7 @@ var _ = ginkgo.Describe("CREATE STREAM:", func() { client := createAuthorizedConnection() name := createRandomString(32) - stream, err := client.CreateStream(name) + stream, err := client.CreateStream(context.Background(), name) defer deleteStreamAfterTests(stream.Id, client) itShouldNotReturnError(err) @@ -39,13 +41,13 @@ var _ = ginkgo.Describe("CREATE STREAM:", func() { client := createAuthorizedConnection() name := createRandomString(32) - stream, err := client.CreateStream(name) + stream, err := client.CreateStream(context.Background(), name) defer deleteStreamAfterTests(stream.Id, client) itShouldNotReturnError(err) itShouldSuccessfullyCreateStream(stream.Id, name, client) - _, err = client.CreateStream(name) + _, err = client.CreateStream(context.Background(), name) itShouldReturnSpecificError(err, ierror.ErrStreamNameAlreadyExists) }) @@ -54,7 +56,7 @@ var _ = ginkgo.Describe("CREATE STREAM:", func() { client := createAuthorizedConnection() name := createRandomString(256) - _, err := client.CreateStream(name) + _, err := client.CreateStream(context.Background(), name) itShouldReturnSpecificError(err, ierror.ErrInvalidStreamName) }) @@ -63,7 +65,7 @@ var _ = ginkgo.Describe("CREATE STREAM:", func() { ginkgo.When("User is not logged in", func() { ginkgo.Context("and tries to create stream", func() { client := createClient() - _, err := client.CreateStream(createRandomString(32)) + _, err := client.CreateStream(context.Background(), createRandomString(32)) itShouldReturnUnauthenticatedError(err) }) diff --git a/bdd/go/tests/tcp_test/stream_feature_delete.go b/bdd/go/tests/tcp_test/stream_feature_delete.go index 17a7f85113..2acd9e9a2e 100644 --- a/bdd/go/tests/tcp_test/stream_feature_delete.go +++ b/bdd/go/tests/tcp_test/stream_feature_delete.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" ierror "github.com/apache/iggy/foreign/go/errors" "github.com/onsi/ginkgo/v2" @@ -30,7 +32,7 @@ var _ = ginkgo.Describe("DELETE STREAM:", func() { client := createAuthorizedConnection() streamId, _ := successfullyCreateStream(prefix, client) streamIdentifier, _ := iggcon.NewIdentifier(streamId) - err := client.DeleteStream(streamIdentifier) + err := client.DeleteStream(context.Background(), streamIdentifier) itShouldNotReturnError(err) itShouldSuccessfullyDeleteStream(streamId, client) @@ -39,7 +41,7 @@ var _ = ginkgo.Describe("DELETE STREAM:", func() { ginkgo.Context("and tries to delete non-existing stream", func() { client := createAuthorizedConnection() - err := client.DeleteStream(randomU32Identifier()) + err := client.DeleteStream(context.Background(), randomU32Identifier()) itShouldReturnSpecificError(err, ierror.ErrStreamIdNotFound) }) @@ -48,7 +50,7 @@ var _ = ginkgo.Describe("DELETE STREAM:", func() { ginkgo.When("User is not logged in", func() { ginkgo.Context("and tries to delete stream", func() { client := createClient() - err := client.DeleteStream(randomU32Identifier()) + err := client.DeleteStream(context.Background(), randomU32Identifier()) itShouldReturnUnauthenticatedError(err) }) diff --git a/bdd/go/tests/tcp_test/stream_feature_get_all.go b/bdd/go/tests/tcp_test/stream_feature_get_all.go index e59f28af2d..e4c5e85c47 100644 --- a/bdd/go/tests/tcp_test/stream_feature_get_all.go +++ b/bdd/go/tests/tcp_test/stream_feature_get_all.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + "github.com/onsi/ginkgo/v2" ) @@ -28,7 +30,7 @@ var _ = ginkgo.Describe("GET ALL STREAMS:", func() { client := createAuthorizedConnection() streamId, name := successfullyCreateStream(prefix, client) defer deleteStreamAfterTests(streamId, client) - streams, err := client.GetStreams() + streams, err := client.GetStreams(context.Background()) itShouldNotReturnError(err) itShouldContainSpecificStream(streamId, name, streams) @@ -38,7 +40,7 @@ var _ = ginkgo.Describe("GET ALL STREAMS:", func() { ginkgo.When("User is not logged in", func() { ginkgo.Context("and tries to get all streams", func() { client := createClient() - _, err := client.GetStreams() + _, err := client.GetStreams(context.Background()) itShouldReturnUnauthenticatedError(err) }) diff --git a/bdd/go/tests/tcp_test/stream_feature_get_by_id.go b/bdd/go/tests/tcp_test/stream_feature_get_by_id.go index 60e082771a..41e7e62e64 100644 --- a/bdd/go/tests/tcp_test/stream_feature_get_by_id.go +++ b/bdd/go/tests/tcp_test/stream_feature_get_by_id.go @@ -18,6 +18,7 @@ package tcp_test import ( + "context" "math" iggcon "github.com/apache/iggy/foreign/go/contracts" @@ -34,7 +35,7 @@ var _ = ginkgo.Describe("GET STREAM BY ID:", func() { streamId, name := successfullyCreateStream(prefix, client) defer deleteStreamAfterTests(streamId, client) streamIdentifier, _ := iggcon.NewIdentifier(streamId) - stream, err := client.GetStream(streamIdentifier) + stream, err := client.GetStream(context.Background(), streamIdentifier) itShouldNotReturnError(err) itShouldReturnSpecificStream(streamId, name, *stream) @@ -43,7 +44,7 @@ var _ = ginkgo.Describe("GET STREAM BY ID:", func() { ginkgo.Context("and tries to get non-existing stream", func() { client := createAuthorizedConnection() - _, err := client.GetStream(randomU32Identifier()) + _, err := client.GetStream(context.Background(), randomU32Identifier()) itShouldReturnSpecificError(err, ierror.ErrStreamIdNotFound) }) @@ -57,7 +58,7 @@ var _ = ginkgo.Describe("GET STREAM BY ID:", func() { // create two topics t1Name := createRandomString(32) t2Name := createRandomString(32) - t1, err := client.CreateTopic(streamIdentifier, + t1, err := client.CreateTopic(context.Background(), streamIdentifier, t1Name, 2, iggcon.CompressionAlgorithmNone, @@ -66,6 +67,7 @@ var _ = ginkgo.Describe("GET STREAM BY ID:", func() { nil) itShouldNotReturnError(err) t2, err := client.CreateTopic( + context.Background(), streamIdentifier, t2Name, 2, @@ -78,7 +80,7 @@ var _ = ginkgo.Describe("GET STREAM BY ID:", func() { itShouldSuccessfullyCreateTopic(streamId, t2.Id, t2Name, client) // check stream details - stream, err := client.GetStream(streamIdentifier) + stream, err := client.GetStream(context.Background(), streamIdentifier) itShouldNotReturnError(err) itShouldReturnSpecificStream(streamId, name, *stream) ginkgo.It("should have exactly 2 topics", func() { diff --git a/bdd/go/tests/tcp_test/stream_feature_update.go b/bdd/go/tests/tcp_test/stream_feature_update.go index b0deb2502b..d8ea7a5dd3 100644 --- a/bdd/go/tests/tcp_test/stream_feature_update.go +++ b/bdd/go/tests/tcp_test/stream_feature_update.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" ierror "github.com/apache/iggy/foreign/go/errors" "github.com/onsi/ginkgo/v2" @@ -32,7 +34,7 @@ var _ = ginkgo.Describe("UPDATE STREAM:", func() { defer deleteStreamAfterTests(streamId, client) newName := createRandomString(128) streamIdentifier, _ := iggcon.NewIdentifier(streamId) - err := client.UpdateStream(streamIdentifier, newName) + err := client.UpdateStream(context.Background(), streamIdentifier, newName) itShouldNotReturnError(err) itShouldSuccessfullyUpdateStream(streamId, newName, client) }) @@ -45,14 +47,14 @@ var _ = ginkgo.Describe("UPDATE STREAM:", func() { defer deleteStreamAfterTests(stream2Id, client) stream2Identifier, _ := iggcon.NewIdentifier(stream2Id) - err := client.UpdateStream(stream2Identifier, stream1Name) + err := client.UpdateStream(context.Background(), stream2Identifier, stream1Name) itShouldReturnSpecificError(err, ierror.ErrStreamNameAlreadyExists) }) ginkgo.Context("and tries to update non-existing stream", func() { client := createAuthorizedConnection() - err := client.UpdateStream(randomU32Identifier(), createRandomString(128)) + err := client.UpdateStream(context.Background(), randomU32Identifier(), createRandomString(128)) itShouldReturnSpecificError(err, ierror.ErrStreamIdNotFound) }) @@ -62,7 +64,7 @@ var _ = ginkgo.Describe("UPDATE STREAM:", func() { streamId, _ := successfullyCreateStream(prefix, client) defer deleteStreamAfterTests(streamId, createAuthorizedConnection()) streamIdentifier, _ := iggcon.NewIdentifier(streamId) - err := client.UpdateStream(streamIdentifier, createRandomString(256)) + err := client.UpdateStream(context.Background(), streamIdentifier, createRandomString(256)) itShouldReturnSpecificError(err, ierror.ErrInvalidStreamName) }) @@ -71,7 +73,7 @@ var _ = ginkgo.Describe("UPDATE STREAM:", func() { ginkgo.When("User is not logged in", func() { ginkgo.Context("and tries to update stream", func() { client := createClient() - err := client.UpdateStream(randomU32Identifier(), createRandomString(128)) + err := client.UpdateStream(context.Background(), randomU32Identifier(), createRandomString(128)) itShouldReturnUnauthenticatedError(err) }) diff --git a/bdd/go/tests/tcp_test/stream_steps.go b/bdd/go/tests/tcp_test/stream_steps.go index 0058f12197..2789bf4fb3 100644 --- a/bdd/go/tests/tcp_test/stream_steps.go +++ b/bdd/go/tests/tcp_test/stream_steps.go @@ -18,6 +18,7 @@ package tcp_test import ( + "context" "fmt" iggcon "github.com/apache/iggy/foreign/go/contracts" @@ -31,7 +32,7 @@ import ( func successfullyCreateStream(prefix string, client iggcon.Client) (uint32, string) { name := createRandomStringWithPrefix(prefix, 128) - stream, err := client.CreateStream(name) + stream, err := client.CreateStream(context.Background(), name) itShouldNotReturnError(err) itShouldSuccessfullyCreateStream(stream.Id, name, client) @@ -79,7 +80,7 @@ func itShouldContainSpecificStream(id uint32, name string, streams []iggcon.Stre func itShouldSuccessfullyCreateStream(id uint32, expectedName string, client iggcon.Client) { streamIdentifier, _ := iggcon.NewIdentifier(id) - stream, err := client.GetStream(streamIdentifier) + stream, err := client.GetStream(context.Background(), streamIdentifier) itShouldNotReturnError(err) ginkgo.It("should create stream with id "+string(rune(id)), func() { @@ -93,7 +94,7 @@ func itShouldSuccessfullyCreateStream(id uint32, expectedName string, client igg func itShouldSuccessfullyUpdateStream(id uint32, expectedName string, client iggcon.Client) { streamIdentifier, _ := iggcon.NewIdentifier(id) - stream, err := client.GetStream(streamIdentifier) + stream, err := client.GetStream(context.Background(), streamIdentifier) itShouldNotReturnError(err) ginkgo.It("should update stream with id "+string(rune(id)), func() { @@ -107,7 +108,7 @@ func itShouldSuccessfullyUpdateStream(id uint32, expectedName string, client igg func itShouldSuccessfullyDeleteStream(id uint32, client iggcon.Client) { streamIdentifier, _ := iggcon.NewIdentifier(id) - stream, err := client.GetStream(streamIdentifier) + stream, err := client.GetStream(context.Background(), streamIdentifier) itShouldReturnSpecificError(err, ierror.ErrStreamIdNotFound) ginkgo.It("should not return stream", func() { @@ -117,5 +118,5 @@ func itShouldSuccessfullyDeleteStream(id uint32, client iggcon.Client) { func deleteStreamAfterTests(streamId uint32, client iggcon.Client) { streamIdentifier, _ := iggcon.NewIdentifier(streamId) - _ = client.DeleteStream(streamIdentifier) + _ = client.DeleteStream(context.Background(), streamIdentifier) } diff --git a/bdd/go/tests/tcp_test/test_helpers.go b/bdd/go/tests/tcp_test/test_helpers.go index 8ee1150a1a..60f8b3067d 100644 --- a/bdd/go/tests/tcp_test/test_helpers.go +++ b/bdd/go/tests/tcp_test/test_helpers.go @@ -18,6 +18,7 @@ package tcp_test import ( + "context" "math/rand" "os" "strings" @@ -31,7 +32,7 @@ import ( func createAuthorizedConnection() iggcon.Client { cli := createClient() - _, err := cli.LoginUser("iggy", "iggy") + _, err := cli.LoginUser(context.Background(), "iggy", "iggy") if err != nil { panic(err) } diff --git a/bdd/go/tests/tcp_test/topic_feature_create.go b/bdd/go/tests/tcp_test/topic_feature_create.go index db85aab553..d5777601ba 100644 --- a/bdd/go/tests/tcp_test/topic_feature_create.go +++ b/bdd/go/tests/tcp_test/topic_feature_create.go @@ -18,6 +18,7 @@ package tcp_test import ( + "context" "math" iggcon "github.com/apache/iggy/foreign/go/contracts" @@ -36,6 +37,7 @@ var _ = ginkgo.Describe("CREATE TOPIC:", func() { defer deleteStreamAfterTests(streamId, client) streamIdentifier, _ := iggcon.NewIdentifier(streamId) _, err := client.CreateTopic( + context.Background(), streamIdentifier, name, 2, @@ -54,6 +56,7 @@ var _ = ginkgo.Describe("CREATE TOPIC:", func() { name := createRandomString(32) streamIdentifier, _ := iggcon.NewIdentifier(streamId) _, err := client.CreateTopic( + context.Background(), streamIdentifier, name, 2, @@ -74,6 +77,7 @@ var _ = ginkgo.Describe("CREATE TOPIC:", func() { replicationFactor := uint8(1) streamIdentifier, _ := iggcon.NewIdentifier(streamId) _, err := client.CreateTopic( + context.Background(), streamIdentifier, name, 2, @@ -92,6 +96,7 @@ var _ = ginkgo.Describe("CREATE TOPIC:", func() { streamIdentifier, _ := iggcon.NewIdentifier(streamId) replicationFactor := uint8(1) _, err := client.CreateTopic( + context.Background(), streamIdentifier, createRandomString(256), 2, @@ -110,6 +115,7 @@ var _ = ginkgo.Describe("CREATE TOPIC:", func() { replicationFactor := uint8(1) streamIdentifier, _ := iggcon.NewIdentifier[uint32](10) _, err := client.CreateTopic( + context.Background(), streamIdentifier, "name", 2, diff --git a/bdd/go/tests/tcp_test/topic_feature_delete.go b/bdd/go/tests/tcp_test/topic_feature_delete.go index b25bd0ccab..b8be81a79a 100644 --- a/bdd/go/tests/tcp_test/topic_feature_delete.go +++ b/bdd/go/tests/tcp_test/topic_feature_delete.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" ierror "github.com/apache/iggy/foreign/go/errors" "github.com/onsi/ginkgo/v2" @@ -33,7 +35,7 @@ var _ = ginkgo.Describe("DELETE TOPIC:", func() { topicId, _ := successfullyCreateTopic(streamId, client) streamIdentifier, _ := iggcon.NewIdentifier(streamId) topicIdentifier, _ := iggcon.NewIdentifier(topicId) - err := client.DeleteTopic(streamIdentifier, topicIdentifier) + err := client.DeleteTopic(context.Background(), streamIdentifier, topicIdentifier) itShouldNotReturnError(err) itShouldSuccessfullyDeleteTopic(streamId, topicId, client) @@ -44,7 +46,7 @@ var _ = ginkgo.Describe("DELETE TOPIC:", func() { streamId, _ := successfullyCreateStream(prefix, client) defer deleteStreamAfterTests(streamId, client) streamIdentifier, _ := iggcon.NewIdentifier(streamId) - err := client.DeleteTopic(streamIdentifier, randomU32Identifier()) + err := client.DeleteTopic(context.Background(), streamIdentifier, randomU32Identifier()) itShouldReturnSpecificError(err, ierror.ErrTopicIdNotFound) }) @@ -52,7 +54,7 @@ var _ = ginkgo.Describe("DELETE TOPIC:", func() { ginkgo.Context("and tries to delete non-existing topic and stream", func() { client := createAuthorizedConnection() - err := client.DeleteTopic(randomU32Identifier(), randomU32Identifier()) + err := client.DeleteTopic(context.Background(), randomU32Identifier(), randomU32Identifier()) itShouldReturnSpecificError(err, ierror.ErrStreamIdNotFound) }) @@ -61,7 +63,7 @@ var _ = ginkgo.Describe("DELETE TOPIC:", func() { ginkgo.When("User is not logged in", func() { ginkgo.Context("and tries to delete topic", func() { client := createClient() - err := client.DeleteTopic(randomU32Identifier(), randomU32Identifier()) + err := client.DeleteTopic(context.Background(), randomU32Identifier(), randomU32Identifier()) itShouldReturnUnauthenticatedError(err) }) diff --git a/bdd/go/tests/tcp_test/topic_feature_get_all.go b/bdd/go/tests/tcp_test/topic_feature_get_all.go index 16ee403ba1..5db23c8293 100644 --- a/bdd/go/tests/tcp_test/topic_feature_get_all.go +++ b/bdd/go/tests/tcp_test/topic_feature_get_all.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" "github.com/onsi/ginkgo/v2" ) @@ -31,7 +33,7 @@ var _ = ginkgo.Describe("GET ALL TOPICS:", func() { defer deleteStreamAfterTests(streamId, client) topicId, name := successfullyCreateTopic(streamId, client) streamIdentifier, _ := iggcon.NewIdentifier(streamId) - topics, err := client.GetTopics(streamIdentifier) + topics, err := client.GetTopics(context.Background(), streamIdentifier) itShouldNotReturnError(err) itShouldContainSpecificTopic(topicId, name, topics) @@ -41,7 +43,7 @@ var _ = ginkgo.Describe("GET ALL TOPICS:", func() { ginkgo.When("User is not logged in", func() { ginkgo.Context("and tries to get all topics", func() { client := createClient() - _, err := client.GetTopics(randomU32Identifier()) + _, err := client.GetTopics(context.Background(), randomU32Identifier()) itShouldReturnUnauthenticatedError(err) }) diff --git a/bdd/go/tests/tcp_test/topic_feature_get_by_id.go b/bdd/go/tests/tcp_test/topic_feature_get_by_id.go index d2c62daccc..5d4e82a935 100644 --- a/bdd/go/tests/tcp_test/topic_feature_get_by_id.go +++ b/bdd/go/tests/tcp_test/topic_feature_get_by_id.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" ierror "github.com/apache/iggy/foreign/go/errors" "github.com/onsi/ginkgo/v2" @@ -33,7 +35,7 @@ var _ = ginkgo.Describe("GET TOPIC BY ID:", func() { topicId, name := successfullyCreateTopic(streamId, client) streamIdentifier, _ := iggcon.NewIdentifier(streamId) topicIdentifier, _ := iggcon.NewIdentifier(topicId) - topic, err := client.GetTopic(streamIdentifier, topicIdentifier) + topic, err := client.GetTopic(context.Background(), streamIdentifier, topicIdentifier) itShouldNotReturnError(err) itShouldReturnSpecificTopic(topicId, name, *topic) @@ -42,7 +44,7 @@ var _ = ginkgo.Describe("GET TOPIC BY ID:", func() { ginkgo.Context("and tries to get topic from non-existing stream", func() { client := createAuthorizedConnection() - _, err := client.GetTopic(randomU32Identifier(), randomU32Identifier()) + _, err := client.GetTopic(context.Background(), randomU32Identifier(), randomU32Identifier()) itShouldReturnSpecificError(err, ierror.ErrTopicIdNotFound) }) @@ -53,7 +55,7 @@ var _ = ginkgo.Describe("GET TOPIC BY ID:", func() { defer deleteStreamAfterTests(streamId, client) streamIdentifier, _ := iggcon.NewIdentifier(streamId) - _, err := client.GetTopic(streamIdentifier, randomU32Identifier()) + _, err := client.GetTopic(context.Background(), streamIdentifier, randomU32Identifier()) itShouldReturnSpecificError(err, ierror.ErrTopicIdNotFound) }) diff --git a/bdd/go/tests/tcp_test/topic_feature_update.go b/bdd/go/tests/tcp_test/topic_feature_update.go index 5059a0d3c3..e8ce2e527e 100644 --- a/bdd/go/tests/tcp_test/topic_feature_update.go +++ b/bdd/go/tests/tcp_test/topic_feature_update.go @@ -18,6 +18,7 @@ package tcp_test import ( + "context" "math" iggcon "github.com/apache/iggy/foreign/go/contracts" @@ -38,6 +39,7 @@ var _ = ginkgo.Describe("UPDATE TOPIC:", func() { streamIdentifier, _ := iggcon.NewIdentifier(streamId) topicIdentifier, _ := iggcon.NewIdentifier(topicId) err := client.UpdateTopic( + context.Background(), streamIdentifier, topicIdentifier, newName, @@ -59,6 +61,7 @@ var _ = ginkgo.Describe("UPDATE TOPIC:", func() { streamIdentifier, _ := iggcon.NewIdentifier(streamId) topic2Identifier, _ := iggcon.NewIdentifier(topic2Id) err := client.UpdateTopic( + context.Background(), streamIdentifier, topic2Identifier, topic1Name, @@ -74,6 +77,7 @@ var _ = ginkgo.Describe("UPDATE TOPIC:", func() { client := createAuthorizedConnection() replicationFactor := uint8(1) err := client.UpdateTopic( + context.Background(), randomU32Identifier(), randomU32Identifier(), createRandomString(128), @@ -92,6 +96,7 @@ var _ = ginkgo.Describe("UPDATE TOPIC:", func() { replicationFactor := uint8(1) streamIdentifier, _ := iggcon.NewIdentifier(streamId) err := client.UpdateTopic( + context.Background(), streamIdentifier, randomU32Identifier(), createRandomString(128), @@ -112,6 +117,7 @@ var _ = ginkgo.Describe("UPDATE TOPIC:", func() { streamIdentifier, _ := iggcon.NewIdentifier(streamId) topicIdentifier, _ := iggcon.NewIdentifier(topicId) err := client.UpdateTopic( + context.Background(), streamIdentifier, topicIdentifier, createRandomString(256), @@ -127,7 +133,7 @@ var _ = ginkgo.Describe("UPDATE TOPIC:", func() { ginkgo.When("User is not logged in", func() { ginkgo.Context("and tries to update stream", func() { client := createClient() - err := client.UpdateStream(randomU32Identifier(), createRandomString(128)) + err := client.UpdateStream(context.Background(), randomU32Identifier(), createRandomString(128)) itShouldReturnUnauthenticatedError(err) }) diff --git a/bdd/go/tests/tcp_test/topic_steps.go b/bdd/go/tests/tcp_test/topic_steps.go index a380d7c832..fae8d6a40c 100644 --- a/bdd/go/tests/tcp_test/topic_steps.go +++ b/bdd/go/tests/tcp_test/topic_steps.go @@ -18,6 +18,7 @@ package tcp_test import ( + "context" "fmt" "math" @@ -34,6 +35,7 @@ func successfullyCreateTopic(streamId uint32, client iggcon.Client) (uint32, str name := createRandomString(128) streamIdentifier, _ := iggcon.NewIdentifier(streamId) topic, err := client.CreateTopic( + context.Background(), streamIdentifier, name, 2, @@ -89,7 +91,7 @@ func itShouldContainSpecificTopic(id uint32, name string, topics []iggcon.Topic) func itShouldSuccessfullyCreateTopic(streamId uint32, topicId uint32, expectedName string, client iggcon.Client) { streamIdentifier, _ := iggcon.NewIdentifier(streamId) topicIdentifier, _ := iggcon.NewIdentifier(topicId) - topic, err := client.GetTopic(streamIdentifier, topicIdentifier) + topic, err := client.GetTopic(context.Background(), streamIdentifier, topicIdentifier) ginkgo.It("should create topic with id "+string(rune(topicId)), func() { gomega.Expect(topic).NotTo(gomega.BeNil()) gomega.Expect(topic.Id).To(gomega.Equal(topicId)) @@ -105,7 +107,7 @@ func itShouldSuccessfullyCreateTopic(streamId uint32, topicId uint32, expectedNa func itShouldSuccessfullyUpdateTopic(streamId uint32, topicId uint32, expectedName string, client iggcon.Client) { streamIdentifier, _ := iggcon.NewIdentifier(streamId) topicIdentifier, _ := iggcon.NewIdentifier(topicId) - topic, err := client.GetTopic(streamIdentifier, topicIdentifier) + topic, err := client.GetTopic(context.Background(), streamIdentifier, topicIdentifier) ginkgo.It("should update topic with id "+string(rune(topicId)), func() { gomega.Expect(topic).NotTo(gomega.BeNil()) @@ -122,7 +124,7 @@ func itShouldSuccessfullyUpdateTopic(streamId uint32, topicId uint32, expectedNa func itShouldSuccessfullyDeleteTopic(streamId uint32, topicId uint32, client iggcon.Client) { streamIdentifier, _ := iggcon.NewIdentifier(streamId) topicIdentifier, _ := iggcon.NewIdentifier(topicId) - topic, err := client.GetTopic(streamIdentifier, topicIdentifier) + topic, err := client.GetTopic(context.Background(), streamIdentifier, topicIdentifier) itShouldReturnSpecificError(err, ierror.ErrTopicIdNotFound) ginkgo.It("should not return topic", func() { diff --git a/bdd/go/tests/tcp_test/users_feature_create.go b/bdd/go/tests/tcp_test/users_feature_create.go index 11d40e023c..033267c3d9 100644 --- a/bdd/go/tests/tcp_test/users_feature_create.go +++ b/bdd/go/tests/tcp_test/users_feature_create.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" "github.com/onsi/ginkgo/v2" ) @@ -29,6 +31,7 @@ var _ = ginkgo.Describe("CREATE USER:", func() { username := createRandomString(16) _, err := client.CreateUser( + context.Background(), username, createRandomString(16), iggcon.Active, @@ -83,6 +86,7 @@ var _ = ginkgo.Describe("CREATE USER:", func() { username := createRandomString(16) _, err := client.CreateUser( + context.Background(), username, createRandomString(16), iggcon.Active, @@ -116,6 +120,7 @@ var _ = ginkgo.Describe("CREATE USER:", func() { client := createClient() _, err := client.CreateUser( + context.Background(), createRandomString(16), createRandomString(16), iggcon.Active, diff --git a/bdd/go/tests/tcp_test/users_feature_delete.go b/bdd/go/tests/tcp_test/users_feature_delete.go index ce3de1997c..f253407c23 100644 --- a/bdd/go/tests/tcp_test/users_feature_delete.go +++ b/bdd/go/tests/tcp_test/users_feature_delete.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" "github.com/onsi/ginkgo/v2" ) @@ -28,7 +30,7 @@ var _ = ginkgo.Describe("DELETE USER:", func() { client := createAuthorizedConnection() userId := successfullyCreateUser(createRandomString(16), client) userIdentifier, _ := iggcon.NewIdentifier(userId) - err := client.DeleteUser(userIdentifier) + err := client.DeleteUser(context.Background(), userIdentifier) itShouldNotReturnError(err) itShouldSuccessfullyDeleteUser(userId, client) @@ -38,7 +40,7 @@ var _ = ginkgo.Describe("DELETE USER:", func() { ginkgo.When("User is not logged in", func() { ginkgo.Context("and tries to delete user", func() { client := createClient() - err := client.DeleteUser(randomU32Identifier()) + err := client.DeleteUser(context.Background(), randomU32Identifier()) itShouldReturnUnauthenticatedError(err) }) }) diff --git a/bdd/go/tests/tcp_test/users_feature_get_all.go b/bdd/go/tests/tcp_test/users_feature_get_all.go index 60c508f5a2..b16a578da6 100644 --- a/bdd/go/tests/tcp_test/users_feature_get_all.go +++ b/bdd/go/tests/tcp_test/users_feature_get_all.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" "github.com/onsi/ginkgo/v2" ) @@ -31,7 +33,7 @@ var _ = ginkgo.Describe("GET USER:", func() { userIdentifier, _ := iggcon.NewIdentifier(userId) defer deleteUserAfterTests(userIdentifier, client) - users, err := client.GetUsers() + users, err := client.GetUsers(context.Background()) itShouldNotReturnError(err) itShouldContainSpecificUser(name, users) @@ -41,7 +43,7 @@ var _ = ginkgo.Describe("GET USER:", func() { ginkgo.When("User is not logged in", func() { ginkgo.Context("and tries to all get users", func() { client := createClient() - _, err := client.GetUsers() + _, err := client.GetUsers(context.Background()) itShouldReturnUnauthenticatedError(err) }) }) diff --git a/bdd/go/tests/tcp_test/users_feature_get_by_id.go b/bdd/go/tests/tcp_test/users_feature_get_by_id.go index 18d2c90572..bcd49a76bd 100644 --- a/bdd/go/tests/tcp_test/users_feature_get_by_id.go +++ b/bdd/go/tests/tcp_test/users_feature_get_by_id.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" "github.com/onsi/ginkgo/v2" ) @@ -31,7 +33,7 @@ var _ = ginkgo.Describe("GET USER:", func() { userIdentifier, _ := iggcon.NewIdentifier(userId) defer deleteUserAfterTests(userIdentifier, client) - user, err := client.GetUser(userIdentifier) + user, err := client.GetUser(context.Background(), userIdentifier) itShouldNotReturnError(err) itShouldReturnSpecificUser(name, user.UserInfo) diff --git a/bdd/go/tests/tcp_test/users_feature_password.go b/bdd/go/tests/tcp_test/users_feature_password.go index 4d6013d871..78d756235c 100644 --- a/bdd/go/tests/tcp_test/users_feature_password.go +++ b/bdd/go/tests/tcp_test/users_feature_password.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" "github.com/onsi/ginkgo/v2" ) @@ -30,6 +32,7 @@ var _ = ginkgo.Describe("CHANGE PASSWORD:", func() { username := createRandomStringWithPrefix("ch_p_", 16) password := "oldPassword" _, err := client.CreateUser( + context.Background(), username, password, iggcon.Active, @@ -51,7 +54,7 @@ var _ = ginkgo.Describe("CHANGE PASSWORD:", func() { identifier, _ := iggcon.NewIdentifier(username) defer deleteUserAfterTests(identifier, client) - err = client.ChangePassword(identifier, password, "newPassword") + err = client.ChangePassword(context.Background(), identifier, password, "newPassword") itShouldNotReturnError(err) //itShouldBePossibleToLogInWithCredentials(createRequest.Username, request.NewPassword) @@ -63,6 +66,7 @@ var _ = ginkgo.Describe("CHANGE PASSWORD:", func() { client := createClient() err := client.UpdatePermissions( + context.Background(), randomU32Identifier(), &iggcon.Permissions{ Global: iggcon.GlobalPermissions{ diff --git a/bdd/go/tests/tcp_test/users_feature_permissions.go b/bdd/go/tests/tcp_test/users_feature_permissions.go index 972b0d938f..55b734ca80 100644 --- a/bdd/go/tests/tcp_test/users_feature_permissions.go +++ b/bdd/go/tests/tcp_test/users_feature_permissions.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" "github.com/onsi/ginkgo/v2" ) @@ -31,6 +33,7 @@ var _ = ginkgo.Describe("UPDATE USER PERMISSIONS:", func() { defer deleteUserAfterTests(identifier, client) err := client.UpdatePermissions( + context.Background(), identifier, &iggcon.Permissions{ Global: iggcon.GlobalPermissions{ @@ -57,6 +60,7 @@ var _ = ginkgo.Describe("UPDATE USER PERMISSIONS:", func() { client := createClient() username := createRandomString(16) err := client.UpdateUser( + context.Background(), randomU32Identifier(), &username, nil, diff --git a/bdd/go/tests/tcp_test/users_feature_update.go b/bdd/go/tests/tcp_test/users_feature_update.go index 94efbbfb3c..2a6df4cbac 100644 --- a/bdd/go/tests/tcp_test/users_feature_update.go +++ b/bdd/go/tests/tcp_test/users_feature_update.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" "github.com/onsi/ginkgo/v2" ) @@ -32,6 +34,7 @@ var _ = ginkgo.Describe("UPDATE USER:", func() { username := createRandomString(16) err := client.UpdateUser( + context.Background(), identifier, &username, nil, @@ -48,6 +51,7 @@ var _ = ginkgo.Describe("UPDATE USER:", func() { username := createRandomString(16) err := client.UpdateUser( + context.Background(), randomU32Identifier(), &username, nil, diff --git a/bdd/go/tests/tcp_test/users_steps.go b/bdd/go/tests/tcp_test/users_steps.go index 6230546ea1..22a71dc99e 100644 --- a/bdd/go/tests/tcp_test/users_steps.go +++ b/bdd/go/tests/tcp_test/users_steps.go @@ -18,6 +18,8 @@ package tcp_test import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" ierror "github.com/apache/iggy/foreign/go/errors" "github.com/onsi/ginkgo/v2" @@ -28,6 +30,7 @@ import ( func successfullyCreateUser(name string, client iggcon.Client) uint32 { _, err := client.CreateUser( + context.Background(), name, createRandomString(16), iggcon.Active, @@ -47,7 +50,7 @@ func successfullyCreateUser(name string, client iggcon.Client) uint32 { }) itShouldNotReturnError(err) nameIdentifier, _ := iggcon.NewIdentifier(name) - user, err := client.GetUser(nameIdentifier) + user, err := client.GetUser(context.Background(), nameIdentifier) itShouldNotReturnError(err) return user.Id @@ -57,7 +60,7 @@ func successfullyCreateUser(name string, client iggcon.Client) uint32 { func itShouldSuccessfullyCreateUser(name string, client iggcon.Client) { nameIdentifier, _ := iggcon.NewIdentifier(name) - user, err := client.GetUser(nameIdentifier) + user, err := client.GetUser(context.Background(), nameIdentifier) itShouldNotReturnError(err) @@ -68,7 +71,7 @@ func itShouldSuccessfullyCreateUser(name string, client iggcon.Client) { func itShouldSuccessfullyCreateUserWithPermissions(name string, client iggcon.Client, permissions map[int]*iggcon.StreamPermissions) { nameIdentifier, _ := iggcon.NewIdentifier(name) - user, err := client.GetUser(nameIdentifier) + user, err := client.GetUser(context.Background(), nameIdentifier) itShouldNotReturnError(err) @@ -99,7 +102,7 @@ func itShouldSuccessfullyCreateUserWithPermissions(name string, client iggcon.Cl func itShouldSuccessfullyUpdateUser(id uint32, name string, client iggcon.Client) { nameIdentifier, _ := iggcon.NewIdentifier(name) - user, err := client.GetUser(nameIdentifier) + user, err := client.GetUser(context.Background(), nameIdentifier) itShouldNotReturnError(err) @@ -114,7 +117,7 @@ func itShouldSuccessfullyUpdateUser(id uint32, name string, client iggcon.Client func itShouldSuccessfullyDeleteUser(userId uint32, client iggcon.Client) { identifier, _ := iggcon.NewIdentifier(userId) - user, err := client.GetUser(identifier) + user, err := client.GetUser(context.Background(), identifier) itShouldReturnSpecificError(err, ierror.ErrResourceNotFound) ginkgo.It("should not return user", func() { @@ -124,7 +127,7 @@ func itShouldSuccessfullyDeleteUser(userId uint32, client iggcon.Client) { func itShouldSuccessfullyUpdateUserPermissions(userId uint32, client iggcon.Client) { identifier, _ := iggcon.NewIdentifier(userId) - user, err := client.GetUser(identifier) + user, err := client.GetUser(context.Background(), identifier) itShouldNotReturnError(err) @@ -173,5 +176,5 @@ func itShouldContainSpecificUser(name string, users []iggcon.UserInfo) { //CLEANUP func deleteUserAfterTests(identifier iggcon.Identifier, client iggcon.Client) { - _ = client.DeleteUser(identifier) + _ = client.DeleteUser(context.Background(), identifier) } diff --git a/foreign/go/benchmarks/send_messages_benchmark_test.go b/foreign/go/benchmarks/send_messages_benchmark_test.go index 2281e9e833..00dcbac1a9 100644 --- a/foreign/go/benchmarks/send_messages_benchmark_test.go +++ b/foreign/go/benchmarks/send_messages_benchmark_test.go @@ -18,6 +18,7 @@ package benchmarks import ( + "context" "fmt" "math/rand" "sync" @@ -52,7 +53,7 @@ func BenchmarkSendMessage(b *testing.B) { if err != nil { panic("COULD NOT CREATE MESSAGE STREAM") } - _, err = cli.LoginUser("iggy", "iggy") + _, err = cli.LoginUser(context.Background(), "iggy", "iggy") if err != nil { panic("COULD NOT LOG IN") } @@ -105,16 +106,17 @@ func BenchmarkSendMessage(b *testing.B) { func ensureInfrastructureIsInitialized(cli iggcon.Client, streamId uint32) error { streamIdentifier, _ := iggcon.NewIdentifier(streamId) - if _, streamErr := cli.GetStream(streamIdentifier); streamErr != nil { - _, streamErr = cli.CreateStream("benchmark" + fmt.Sprint(streamId)) + if _, streamErr := cli.GetStream(context.Background(), streamIdentifier); streamErr != nil { + _, streamErr = cli.CreateStream(context.Background(), "benchmark"+fmt.Sprint(streamId)) if streamErr != nil { panic(streamErr) } } topicIdentifier, _ := iggcon.NewIdentifier(uint32(0)) - if _, topicErr := cli.GetTopic(streamIdentifier, topicIdentifier); topicErr != nil { + if _, topicErr := cli.GetTopic(context.Background(), streamIdentifier, topicIdentifier); topicErr != nil { _, topicErr = cli.CreateTopic( + context.Background(), streamIdentifier, "benchmark", 1, @@ -133,7 +135,7 @@ func ensureInfrastructureIsInitialized(cli iggcon.Client, streamId uint32) error func cleanupInfrastructure(cli iggcon.Client, streamId uint32) error { streamIdent, _ := iggcon.NewIdentifier(streamId) - return cli.DeleteStream(streamIdent) + return cli.DeleteStream(context.Background(), streamIdent) } // CreateMessages creates messages with random payloads. @@ -169,6 +171,7 @@ func SendMessage(cli iggcon.Client, producerNumber, messagesCount, messagesBatch startTime := time.Now() topicIdentifier, _ := iggcon.NewIdentifier(uint32(topicId)) _ = cli.SendMessages( + context.Background(), streamId, topicIdentifier, iggcon.PartitionId(1), diff --git a/foreign/go/client/iggy_client.go b/foreign/go/client/iggy_client.go index a0f3707231..07959bd778 100644 --- a/foreign/go/client/iggy_client.go +++ b/foreign/go/client/iggy_client.go @@ -88,7 +88,7 @@ func NewIggyClient(options ...Option) (iggcon.Client, error) { case <-ctx.Done(): return case <-ticker.C: - if err := cli.Ping(); err != nil { + if err := cli.Ping(context.Background()); err != nil { log.Printf("[WARN] heartbeat failed: %v", err) } } diff --git a/foreign/go/client/tcp/cluster.go b/foreign/go/client/tcp/cluster.go index af3e332509..2c6965adba 100644 --- a/foreign/go/client/tcp/cluster.go +++ b/foreign/go/client/tcp/cluster.go @@ -18,12 +18,14 @@ package tcp import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" "github.com/apache/iggy/foreign/go/internal/command" ) -func (c *IggyTcpClient) GetClusterMetadata() (*iggcon.ClusterMetadata, error) { - response, err := c.do(&command.GetClusterMetadata{}) +func (c *IggyTcpClient) GetClusterMetadata(ctx context.Context) (*iggcon.ClusterMetadata, error) { + response, err := c.do(ctx, &command.GetClusterMetadata{}) if err != nil { return nil, err } diff --git a/foreign/go/client/tcp/tcp_access_token_management.go b/foreign/go/client/tcp/tcp_access_token_management.go index 68d14a3313..61ba168615 100644 --- a/foreign/go/client/tcp/tcp_access_token_management.go +++ b/foreign/go/client/tcp/tcp_access_token_management.go @@ -18,13 +18,15 @@ package tcp import ( + "context" + binaryserialization "github.com/apache/iggy/foreign/go/binary_serialization" iggcon "github.com/apache/iggy/foreign/go/contracts" "github.com/apache/iggy/foreign/go/internal/command" ) -func (c *IggyTcpClient) CreatePersonalAccessToken(name string, expiry uint32) (*iggcon.RawPersonalAccessToken, error) { - buffer, err := c.do(&command.CreatePersonalAccessToken{ +func (c *IggyTcpClient) CreatePersonalAccessToken(ctx context.Context, name string, expiry uint32) (*iggcon.RawPersonalAccessToken, error) { + buffer, err := c.do(ctx, &command.CreatePersonalAccessToken{ Name: name, Expiry: expiry, }) @@ -35,15 +37,15 @@ func (c *IggyTcpClient) CreatePersonalAccessToken(name string, expiry uint32) (* return binaryserialization.DeserializeAccessToken(buffer) } -func (c *IggyTcpClient) DeletePersonalAccessToken(name string) error { - _, err := c.do(&command.DeletePersonalAccessToken{ +func (c *IggyTcpClient) DeletePersonalAccessToken(ctx context.Context, name string) error { + _, err := c.do(ctx, &command.DeletePersonalAccessToken{ Name: name, }) return err } -func (c *IggyTcpClient) GetPersonalAccessTokens() ([]iggcon.PersonalAccessTokenInfo, error) { - buffer, err := c.do(&command.GetPersonalAccessTokens{}) +func (c *IggyTcpClient) GetPersonalAccessTokens(ctx context.Context) ([]iggcon.PersonalAccessTokenInfo, error) { + buffer, err := c.do(ctx, &command.GetPersonalAccessTokens{}) if err != nil { return nil, err } diff --git a/foreign/go/client/tcp/tcp_clients_management.go b/foreign/go/client/tcp/tcp_clients_management.go index 81f0427ab2..4f0de678b9 100644 --- a/foreign/go/client/tcp/tcp_clients_management.go +++ b/foreign/go/client/tcp/tcp_clients_management.go @@ -18,13 +18,15 @@ package tcp import ( + "context" + binaryserialization "github.com/apache/iggy/foreign/go/binary_serialization" iggcon "github.com/apache/iggy/foreign/go/contracts" "github.com/apache/iggy/foreign/go/internal/command" ) -func (c *IggyTcpClient) GetClients() ([]iggcon.ClientInfo, error) { - buffer, err := c.do(&command.GetClients{}) +func (c *IggyTcpClient) GetClients(ctx context.Context) ([]iggcon.ClientInfo, error) { + buffer, err := c.do(ctx, &command.GetClients{}) if err != nil { return nil, err } @@ -32,8 +34,8 @@ func (c *IggyTcpClient) GetClients() ([]iggcon.ClientInfo, error) { return binaryserialization.DeserializeClients(buffer) } -func (c *IggyTcpClient) GetClient(clientId uint32) (*iggcon.ClientInfoDetails, error) { - buffer, err := c.do(&command.GetClient{ClientID: clientId}) +func (c *IggyTcpClient) GetClient(ctx context.Context, clientId uint32) (*iggcon.ClientInfoDetails, error) { + buffer, err := c.do(ctx, &command.GetClient{ClientID: clientId}) if err != nil { return nil, err } diff --git a/foreign/go/client/tcp/tcp_consumer_group_management.go b/foreign/go/client/tcp/tcp_consumer_group_management.go index 131dd1afbd..7f918b7fd3 100644 --- a/foreign/go/client/tcp/tcp_consumer_group_management.go +++ b/foreign/go/client/tcp/tcp_consumer_group_management.go @@ -18,14 +18,16 @@ package tcp import ( + "context" + binaryserialization "github.com/apache/iggy/foreign/go/binary_serialization" iggcon "github.com/apache/iggy/foreign/go/contracts" ierror "github.com/apache/iggy/foreign/go/errors" "github.com/apache/iggy/foreign/go/internal/command" ) -func (c *IggyTcpClient) GetConsumerGroups(streamId, topicId iggcon.Identifier) ([]iggcon.ConsumerGroup, error) { - buffer, err := c.do(&command.GetConsumerGroups{ +func (c *IggyTcpClient) GetConsumerGroups(ctx context.Context, streamId, topicId iggcon.Identifier) ([]iggcon.ConsumerGroup, error) { + buffer, err := c.do(ctx, &command.GetConsumerGroups{ StreamId: streamId, TopicId: topicId, }) @@ -36,8 +38,8 @@ func (c *IggyTcpClient) GetConsumerGroups(streamId, topicId iggcon.Identifier) ( return binaryserialization.DeserializeConsumerGroups(buffer), err } -func (c *IggyTcpClient) GetConsumerGroup(streamId, topicId, groupId iggcon.Identifier) (*iggcon.ConsumerGroupDetails, error) { - buffer, err := c.do(&command.GetConsumerGroup{ +func (c *IggyTcpClient) GetConsumerGroup(ctx context.Context, streamId, topicId, groupId iggcon.Identifier) (*iggcon.ConsumerGroupDetails, error) { + buffer, err := c.do(ctx, &command.GetConsumerGroup{ TopicPath: command.TopicPath{ StreamId: streamId, TopicId: topicId, @@ -55,11 +57,11 @@ func (c *IggyTcpClient) GetConsumerGroup(streamId, topicId, groupId iggcon.Ident return consumerGroupDetails, err } -func (c *IggyTcpClient) CreateConsumerGroup(streamId iggcon.Identifier, topicId iggcon.Identifier, name string) (*iggcon.ConsumerGroupDetails, error) { +func (c *IggyTcpClient) CreateConsumerGroup(ctx context.Context, streamId iggcon.Identifier, topicId iggcon.Identifier, name string) (*iggcon.ConsumerGroupDetails, error) { if MaxStringLength < len(name) || len(name) == 0 { return nil, ierror.ErrInvalidConsumerGroupName } - buffer, err := c.do(&command.CreateConsumerGroup{ + buffer, err := c.do(ctx, &command.CreateConsumerGroup{ TopicPath: command.TopicPath{ StreamId: streamId, TopicId: topicId, @@ -73,8 +75,8 @@ func (c *IggyTcpClient) CreateConsumerGroup(streamId iggcon.Identifier, topicId return consumerGroup, err } -func (c *IggyTcpClient) DeleteConsumerGroup(streamId iggcon.Identifier, topicId iggcon.Identifier, groupId iggcon.Identifier) error { - _, err := c.do(&command.DeleteConsumerGroup{ +func (c *IggyTcpClient) DeleteConsumerGroup(ctx context.Context, streamId iggcon.Identifier, topicId iggcon.Identifier, groupId iggcon.Identifier) error { + _, err := c.do(ctx, &command.DeleteConsumerGroup{ TopicPath: command.TopicPath{ StreamId: streamId, TopicId: topicId, @@ -84,8 +86,8 @@ func (c *IggyTcpClient) DeleteConsumerGroup(streamId iggcon.Identifier, topicId return err } -func (c *IggyTcpClient) JoinConsumerGroup(streamId iggcon.Identifier, topicId iggcon.Identifier, groupId iggcon.Identifier) error { - _, err := c.do(&command.JoinConsumerGroup{ +func (c *IggyTcpClient) JoinConsumerGroup(ctx context.Context, streamId iggcon.Identifier, topicId iggcon.Identifier, groupId iggcon.Identifier) error { + _, err := c.do(ctx, &command.JoinConsumerGroup{ TopicPath: command.TopicPath{ StreamId: streamId, TopicId: topicId, @@ -95,8 +97,8 @@ func (c *IggyTcpClient) JoinConsumerGroup(streamId iggcon.Identifier, topicId ig return err } -func (c *IggyTcpClient) LeaveConsumerGroup(streamId iggcon.Identifier, topicId iggcon.Identifier, groupId iggcon.Identifier) error { - _, err := c.do(&command.LeaveConsumerGroup{ +func (c *IggyTcpClient) LeaveConsumerGroup(ctx context.Context, streamId iggcon.Identifier, topicId iggcon.Identifier, groupId iggcon.Identifier) error { + _, err := c.do(ctx, &command.LeaveConsumerGroup{ TopicPath: command.TopicPath{ StreamId: streamId, TopicId: topicId, diff --git a/foreign/go/client/tcp/tcp_core.go b/foreign/go/client/tcp/tcp_core.go index 91f73f2477..fef53f449a 100644 --- a/foreign/go/client/tcp/tcp_core.go +++ b/foreign/go/client/tcp/tcp_core.go @@ -18,6 +18,7 @@ package tcp import ( + "context" "crypto/tls" "crypto/x509" "encoding/binary" @@ -256,18 +257,29 @@ func (c *IggyTcpClient) write(payload []byte) (int, error) { } // do sends the command to the Iggy server and returns the response. -func (c *IggyTcpClient) do(cmd command.Command) ([]byte, error) { +func (c *IggyTcpClient) do(ctx context.Context, cmd command.Command) ([]byte, error) { data, err := cmd.MarshalBinary() if err != nil { return nil, err } - return c.sendAndFetchResponse(data, cmd.Code()) + return c.sendAndFetchResponse(ctx, data, cmd.Code()) } -func (c *IggyTcpClient) sendAndFetchResponse(message []byte, command command.Code) ([]byte, error) { +func (c *IggyTcpClient) sendAndFetchResponse(ctx context.Context, message []byte, command command.Code) ([]byte, error) { + if err := ctx.Err(); err != nil { + return nil, err + } + c.mtx.Lock() defer c.mtx.Unlock() + if deadline, ok := ctx.Deadline(); ok { + if err := c.conn.SetDeadline(deadline); err != nil { + return nil, err + } + defer func() { _ = c.conn.SetDeadline(time.Time{}) }() + } + payload := createPayload(message, command) if _, err := c.write(payload); err != nil { return nil, err diff --git a/foreign/go/client/tcp/tcp_messaging.go b/foreign/go/client/tcp/tcp_messaging.go index bc8174d922..710cb5b0b4 100644 --- a/foreign/go/client/tcp/tcp_messaging.go +++ b/foreign/go/client/tcp/tcp_messaging.go @@ -18,6 +18,8 @@ package tcp import ( + "context" + binaryserialization "github.com/apache/iggy/foreign/go/binary_serialization" iggcon "github.com/apache/iggy/foreign/go/contracts" ierror "github.com/apache/iggy/foreign/go/errors" @@ -25,6 +27,7 @@ import ( ) func (c *IggyTcpClient) SendMessages( + ctx context.Context, streamId iggcon.Identifier, topicId iggcon.Identifier, partitioning iggcon.Partitioning, @@ -37,7 +40,7 @@ func (c *IggyTcpClient) SendMessages( if len(messages) == 0 { return ierror.ErrInvalidMessagesCount } - _, err := c.do(&command.SendMessages{ + _, err := c.do(ctx, &command.SendMessages{ Compression: c.MessageCompression, StreamId: streamId, TopicId: topicId, @@ -48,6 +51,7 @@ func (c *IggyTcpClient) SendMessages( } func (c *IggyTcpClient) PollMessages( + ctx context.Context, streamId iggcon.Identifier, topicId iggcon.Identifier, consumer iggcon.Consumer, @@ -56,7 +60,7 @@ func (c *IggyTcpClient) PollMessages( autoCommit bool, partitionId *uint32, ) (*iggcon.PolledMessage, error) { - buffer, err := c.do(&command.PollMessages{ + buffer, err := c.do(ctx, &command.PollMessages{ StreamId: streamId, TopicId: topicId, Consumer: consumer, diff --git a/foreign/go/client/tcp/tcp_offset_management.go b/foreign/go/client/tcp/tcp_offset_management.go index bc07582d9a..c2551ae8da 100644 --- a/foreign/go/client/tcp/tcp_offset_management.go +++ b/foreign/go/client/tcp/tcp_offset_management.go @@ -18,13 +18,15 @@ package tcp import ( + "context" + binaryserialization "github.com/apache/iggy/foreign/go/binary_serialization" iggcon "github.com/apache/iggy/foreign/go/contracts" "github.com/apache/iggy/foreign/go/internal/command" ) -func (c *IggyTcpClient) GetConsumerOffset(consumer iggcon.Consumer, streamId iggcon.Identifier, topicId iggcon.Identifier, partitionId *uint32) (*iggcon.ConsumerOffsetInfo, error) { - buffer, err := c.do(&command.GetConsumerOffset{ +func (c *IggyTcpClient) GetConsumerOffset(ctx context.Context, consumer iggcon.Consumer, streamId iggcon.Identifier, topicId iggcon.Identifier, partitionId *uint32) (*iggcon.ConsumerOffsetInfo, error) { + buffer, err := c.do(ctx, &command.GetConsumerOffset{ StreamId: streamId, TopicId: topicId, Consumer: consumer, @@ -37,8 +39,8 @@ func (c *IggyTcpClient) GetConsumerOffset(consumer iggcon.Consumer, streamId igg return binaryserialization.DeserializeOffset(buffer), nil } -func (c *IggyTcpClient) StoreConsumerOffset(consumer iggcon.Consumer, streamId iggcon.Identifier, topicId iggcon.Identifier, offset uint64, partitionId *uint32) error { - _, err := c.do(&command.StoreConsumerOffsetRequest{ +func (c *IggyTcpClient) StoreConsumerOffset(ctx context.Context, consumer iggcon.Consumer, streamId iggcon.Identifier, topicId iggcon.Identifier, offset uint64, partitionId *uint32) error { + _, err := c.do(ctx, &command.StoreConsumerOffsetRequest{ StreamId: streamId, TopicId: topicId, Offset: offset, @@ -48,8 +50,8 @@ func (c *IggyTcpClient) StoreConsumerOffset(consumer iggcon.Consumer, streamId i return err } -func (c *IggyTcpClient) DeleteConsumerOffset(consumer iggcon.Consumer, streamId iggcon.Identifier, topicId iggcon.Identifier, partitionId *uint32) error { - _, err := c.do(&command.DeleteConsumerOffset{ +func (c *IggyTcpClient) DeleteConsumerOffset(ctx context.Context, consumer iggcon.Consumer, streamId iggcon.Identifier, topicId iggcon.Identifier, partitionId *uint32) error { + _, err := c.do(ctx, &command.DeleteConsumerOffset{ Consumer: consumer, StreamId: streamId, TopicId: topicId, diff --git a/foreign/go/client/tcp/tcp_partition_management.go b/foreign/go/client/tcp/tcp_partition_management.go index e42c3be68a..9117687bc4 100644 --- a/foreign/go/client/tcp/tcp_partition_management.go +++ b/foreign/go/client/tcp/tcp_partition_management.go @@ -18,12 +18,14 @@ package tcp import ( + "context" + iggcon "github.com/apache/iggy/foreign/go/contracts" "github.com/apache/iggy/foreign/go/internal/command" ) -func (c *IggyTcpClient) CreatePartitions(streamId iggcon.Identifier, topicId iggcon.Identifier, partitionsCount uint32) error { - _, err := c.do(&command.CreatePartitions{ +func (c *IggyTcpClient) CreatePartitions(ctx context.Context, streamId iggcon.Identifier, topicId iggcon.Identifier, partitionsCount uint32) error { + _, err := c.do(ctx, &command.CreatePartitions{ StreamId: streamId, TopicId: topicId, PartitionsCount: partitionsCount, @@ -31,8 +33,8 @@ func (c *IggyTcpClient) CreatePartitions(streamId iggcon.Identifier, topicId igg return err } -func (c *IggyTcpClient) DeletePartitions(streamId iggcon.Identifier, topicId iggcon.Identifier, partitionsCount uint32) error { - _, err := c.do(&command.DeletePartitions{ +func (c *IggyTcpClient) DeletePartitions(ctx context.Context, streamId iggcon.Identifier, topicId iggcon.Identifier, partitionsCount uint32) error { + _, err := c.do(ctx, &command.DeletePartitions{ StreamId: streamId, TopicId: topicId, PartitionsCount: partitionsCount, diff --git a/foreign/go/client/tcp/tcp_session_management.go b/foreign/go/client/tcp/tcp_session_management.go index 5ec47805db..66df135e89 100644 --- a/foreign/go/client/tcp/tcp_session_management.go +++ b/foreign/go/client/tcp/tcp_session_management.go @@ -18,6 +18,7 @@ package tcp import ( + "context" "time" binaryserialization "github.com/apache/iggy/foreign/go/binary_serialization" @@ -27,8 +28,8 @@ import ( iggcon "github.com/apache/iggy/foreign/go/contracts" ) -func (c *IggyTcpClient) LoginUser(username string, password string) (*iggcon.IdentityInfo, error) { - buffer, err := c.do(&command.LoginUser{ +func (c *IggyTcpClient) LoginUser(ctx context.Context, username string, password string) (*iggcon.IdentityInfo, error) { + buffer, err := c.do(ctx, &command.LoginUser{ Username: username, Password: password, }) @@ -37,7 +38,7 @@ func (c *IggyTcpClient) LoginUser(username string, password string) (*iggcon.Ide } identity := binaryserialization.DeserializeLogInResponse(buffer) - shouldRedirect, err := c.HandleLeaderRedirection() + shouldRedirect, err := c.HandleLeaderRedirection(ctx) if err != nil { return nil, err } @@ -45,13 +46,13 @@ func (c *IggyTcpClient) LoginUser(username string, password string) (*iggcon.Ide if err = c.connect(); err != nil { return nil, err } - return c.LoginUser(username, password) + return c.LoginUser(ctx, username, password) } return identity, nil } -func (c *IggyTcpClient) LoginWithPersonalAccessToken(token string) (*iggcon.IdentityInfo, error) { - buffer, err := c.do(&command.LoginWithPersonalAccessToken{ +func (c *IggyTcpClient) LoginWithPersonalAccessToken(ctx context.Context, token string) (*iggcon.IdentityInfo, error) { + buffer, err := c.do(ctx, &command.LoginWithPersonalAccessToken{ Token: token, }) if err != nil { @@ -59,7 +60,7 @@ func (c *IggyTcpClient) LoginWithPersonalAccessToken(token string) (*iggcon.Iden } identity := binaryserialization.DeserializeLogInResponse(buffer) - shouldRedirect, err := c.HandleLeaderRedirection() + shouldRedirect, err := c.HandleLeaderRedirection(ctx) if err != nil { return nil, err } @@ -67,23 +68,24 @@ func (c *IggyTcpClient) LoginWithPersonalAccessToken(token string) (*iggcon.Iden if err = c.connect(); err != nil { return nil, err } - return c.LoginWithPersonalAccessToken(token) + return c.LoginWithPersonalAccessToken(ctx, token) } return identity, nil } -func (c *IggyTcpClient) LogoutUser() error { - _, err := c.do(&command.LogoutUser{}) +func (c *IggyTcpClient) LogoutUser(ctx context.Context) error { + _, err := c.do(ctx, &command.LogoutUser{}) return err } -func (c *IggyTcpClient) HandleLeaderRedirection() (bool, error) { +func (c *IggyTcpClient) HandleLeaderRedirection(ctx context.Context) (bool, error) { // Clone current address c.mtx.Lock() currentAddress := c.currentServerAddress c.mtx.Unlock() leaderAddress, err := util.CheckAndRedirectToLeader( + ctx, c, currentAddress, iggcon.Tcp, diff --git a/foreign/go/client/tcp/tcp_stream_management.go b/foreign/go/client/tcp/tcp_stream_management.go index 9fbba5b4cc..75b12fc175 100644 --- a/foreign/go/client/tcp/tcp_stream_management.go +++ b/foreign/go/client/tcp/tcp_stream_management.go @@ -18,14 +18,16 @@ package tcp import ( + "context" + binaryserialization "github.com/apache/iggy/foreign/go/binary_serialization" iggcon "github.com/apache/iggy/foreign/go/contracts" ierror "github.com/apache/iggy/foreign/go/errors" "github.com/apache/iggy/foreign/go/internal/command" ) -func (c *IggyTcpClient) GetStreams() ([]iggcon.Stream, error) { - buffer, err := c.do(&command.GetStreams{}) +func (c *IggyTcpClient) GetStreams(ctx context.Context) ([]iggcon.Stream, error) { + buffer, err := c.do(ctx, &command.GetStreams{}) if err != nil { return nil, err } @@ -33,8 +35,8 @@ func (c *IggyTcpClient) GetStreams() ([]iggcon.Stream, error) { return binaryserialization.DeserializeStreams(buffer), nil } -func (c *IggyTcpClient) GetStream(streamId iggcon.Identifier) (*iggcon.StreamDetails, error) { - buffer, err := c.do(&command.GetStream{ +func (c *IggyTcpClient) GetStream(ctx context.Context, streamId iggcon.Identifier) (*iggcon.StreamDetails, error) { + buffer, err := c.do(ctx, &command.GetStream{ StreamId: streamId, }) if err != nil { @@ -52,11 +54,11 @@ func (c *IggyTcpClient) GetStream(streamId iggcon.Identifier) (*iggcon.StreamDet return stream, nil } -func (c *IggyTcpClient) CreateStream(name string) (*iggcon.StreamDetails, error) { +func (c *IggyTcpClient) CreateStream(ctx context.Context, name string) (*iggcon.StreamDetails, error) { if len(name) == 0 || MaxStringLength < len(name) { return nil, ierror.ErrInvalidStreamName } - buffer, err := c.do(&command.CreateStream{Name: name}) + buffer, err := c.do(ctx, &command.CreateStream{Name: name}) if err != nil { return nil, err } @@ -68,15 +70,15 @@ func (c *IggyTcpClient) CreateStream(name string) (*iggcon.StreamDetails, error) return stream, err } -func (c *IggyTcpClient) UpdateStream(streamId iggcon.Identifier, name string) error { +func (c *IggyTcpClient) UpdateStream(ctx context.Context, streamId iggcon.Identifier, name string) error { if len(name) > MaxStringLength || len(name) == 0 { return ierror.ErrInvalidStreamName } - _, err := c.do(&command.UpdateStream{StreamId: streamId, Name: name}) + _, err := c.do(ctx, &command.UpdateStream{StreamId: streamId, Name: name}) return err } -func (c *IggyTcpClient) DeleteStream(id iggcon.Identifier) error { - _, err := c.do(&command.DeleteStream{StreamId: id}) +func (c *IggyTcpClient) DeleteStream(ctx context.Context, id iggcon.Identifier) error { + _, err := c.do(ctx, &command.DeleteStream{StreamId: id}) return err } diff --git a/foreign/go/client/tcp/tcp_topic_management.go b/foreign/go/client/tcp/tcp_topic_management.go index 6bd007aa2f..bfe0ccfee1 100644 --- a/foreign/go/client/tcp/tcp_topic_management.go +++ b/foreign/go/client/tcp/tcp_topic_management.go @@ -18,14 +18,16 @@ package tcp import ( + "context" + binaryserialization "github.com/apache/iggy/foreign/go/binary_serialization" iggcon "github.com/apache/iggy/foreign/go/contracts" ierror "github.com/apache/iggy/foreign/go/errors" "github.com/apache/iggy/foreign/go/internal/command" ) -func (c *IggyTcpClient) GetTopics(streamId iggcon.Identifier) ([]iggcon.Topic, error) { - buffer, err := c.do(&command.GetTopics{StreamId: streamId}) +func (c *IggyTcpClient) GetTopics(ctx context.Context, streamId iggcon.Identifier) ([]iggcon.Topic, error) { + buffer, err := c.do(ctx, &command.GetTopics{StreamId: streamId}) if err != nil { return nil, err } @@ -33,8 +35,8 @@ func (c *IggyTcpClient) GetTopics(streamId iggcon.Identifier) ([]iggcon.Topic, e return binaryserialization.DeserializeTopics(buffer) } -func (c *IggyTcpClient) GetTopic(streamId iggcon.Identifier, topicId iggcon.Identifier) (*iggcon.TopicDetails, error) { - buffer, err := c.do(&command.GetTopic{StreamId: streamId, TopicId: topicId}) +func (c *IggyTcpClient) GetTopic(ctx context.Context, streamId iggcon.Identifier, topicId iggcon.Identifier) (*iggcon.TopicDetails, error) { + buffer, err := c.do(ctx, &command.GetTopic{StreamId: streamId, TopicId: topicId}) if err != nil { return nil, err } @@ -51,6 +53,7 @@ func (c *IggyTcpClient) GetTopic(streamId iggcon.Identifier, topicId iggcon.Iden } func (c *IggyTcpClient) CreateTopic( + ctx context.Context, streamId iggcon.Identifier, name string, partitionsCount uint32, @@ -69,7 +72,7 @@ func (c *IggyTcpClient) CreateTopic( return nil, ierror.ErrInvalidReplicationFactor } - buffer, err := c.do(&command.CreateTopic{ + buffer, err := c.do(ctx, &command.CreateTopic{ StreamId: streamId, Name: name, PartitionsCount: partitionsCount, @@ -86,6 +89,7 @@ func (c *IggyTcpClient) CreateTopic( } func (c *IggyTcpClient) UpdateTopic( + ctx context.Context, streamId iggcon.Identifier, topicId iggcon.Identifier, name string, @@ -100,7 +104,7 @@ func (c *IggyTcpClient) UpdateTopic( if replicationFactor != nil && *replicationFactor == 0 { return ierror.ErrInvalidReplicationFactor } - _, err := c.do(&command.UpdateTopic{ + _, err := c.do(ctx, &command.UpdateTopic{ StreamId: streamId, TopicId: topicId, CompressionAlgorithm: compressionAlgorithm, @@ -112,7 +116,7 @@ func (c *IggyTcpClient) UpdateTopic( return err } -func (c *IggyTcpClient) DeleteTopic(streamId, topicId iggcon.Identifier) error { - _, err := c.do(&command.DeleteTopic{StreamId: streamId, TopicId: topicId}) +func (c *IggyTcpClient) DeleteTopic(ctx context.Context, streamId, topicId iggcon.Identifier) error { + _, err := c.do(ctx, &command.DeleteTopic{StreamId: streamId, TopicId: topicId}) return err } diff --git a/foreign/go/client/tcp/tcp_user_management.go b/foreign/go/client/tcp/tcp_user_management.go index a3575d03c8..bee557b0c7 100644 --- a/foreign/go/client/tcp/tcp_user_management.go +++ b/foreign/go/client/tcp/tcp_user_management.go @@ -18,14 +18,16 @@ package tcp import ( + "context" + binaryserialization "github.com/apache/iggy/foreign/go/binary_serialization" iggcon "github.com/apache/iggy/foreign/go/contracts" ierror "github.com/apache/iggy/foreign/go/errors" "github.com/apache/iggy/foreign/go/internal/command" ) -func (c *IggyTcpClient) GetUser(identifier iggcon.Identifier) (*iggcon.UserInfoDetails, error) { - buffer, err := c.do(&command.GetUser{Id: identifier}) +func (c *IggyTcpClient) GetUser(ctx context.Context, identifier iggcon.Identifier) (*iggcon.UserInfoDetails, error) { + buffer, err := c.do(ctx, &command.GetUser{Id: identifier}) if err != nil { return nil, err } @@ -36,8 +38,8 @@ func (c *IggyTcpClient) GetUser(identifier iggcon.Identifier) (*iggcon.UserInfoD return binaryserialization.DeserializeUser(buffer) } -func (c *IggyTcpClient) GetUsers() ([]iggcon.UserInfo, error) { - buffer, err := c.do(&command.GetUsers{}) +func (c *IggyTcpClient) GetUsers(ctx context.Context) ([]iggcon.UserInfo, error) { + buffer, err := c.do(ctx, &command.GetUsers{}) if err != nil { return nil, err } @@ -45,8 +47,8 @@ func (c *IggyTcpClient) GetUsers() ([]iggcon.UserInfo, error) { return binaryserialization.DeserializeUsers(buffer) } -func (c *IggyTcpClient) CreateUser(username string, password string, status iggcon.UserStatus, permissions *iggcon.Permissions) (*iggcon.UserInfoDetails, error) { - buffer, err := c.do(&command.CreateUser{ +func (c *IggyTcpClient) CreateUser(ctx context.Context, username string, password string, status iggcon.UserStatus, permissions *iggcon.Permissions) (*iggcon.UserInfoDetails, error) { + buffer, err := c.do(ctx, &command.CreateUser{ Username: username, Password: password, Status: status, @@ -62,8 +64,8 @@ func (c *IggyTcpClient) CreateUser(username string, password string, status iggc return userInfo, nil } -func (c *IggyTcpClient) UpdateUser(userID iggcon.Identifier, username *string, status *iggcon.UserStatus) error { - _, err := c.do(&command.UpdateUser{ +func (c *IggyTcpClient) UpdateUser(ctx context.Context, userID iggcon.Identifier, username *string, status *iggcon.UserStatus) error { + _, err := c.do(ctx, &command.UpdateUser{ UserID: userID, Username: username, Status: status, @@ -71,23 +73,23 @@ func (c *IggyTcpClient) UpdateUser(userID iggcon.Identifier, username *string, s return err } -func (c *IggyTcpClient) DeleteUser(identifier iggcon.Identifier) error { - _, err := c.do(&command.DeleteUser{ +func (c *IggyTcpClient) DeleteUser(ctx context.Context, identifier iggcon.Identifier) error { + _, err := c.do(ctx, &command.DeleteUser{ Id: identifier, }) return err } -func (c *IggyTcpClient) UpdatePermissions(userID iggcon.Identifier, permissions *iggcon.Permissions) error { - _, err := c.do(&command.UpdatePermissions{ +func (c *IggyTcpClient) UpdatePermissions(ctx context.Context, userID iggcon.Identifier, permissions *iggcon.Permissions) error { + _, err := c.do(ctx, &command.UpdatePermissions{ UserID: userID, Permissions: permissions, }) return err } -func (c *IggyTcpClient) ChangePassword(userID iggcon.Identifier, currentPassword string, newPassword string) error { - _, err := c.do(&command.ChangePassword{ +func (c *IggyTcpClient) ChangePassword(ctx context.Context, userID iggcon.Identifier, currentPassword string, newPassword string) error { + _, err := c.do(ctx, &command.ChangePassword{ UserID: userID, CurrentPassword: currentPassword, NewPassword: newPassword, diff --git a/foreign/go/client/tcp/tcp_utilities.go b/foreign/go/client/tcp/tcp_utilities.go index 0da1784cb1..1720ac6b9b 100644 --- a/foreign/go/client/tcp/tcp_utilities.go +++ b/foreign/go/client/tcp/tcp_utilities.go @@ -18,13 +18,15 @@ package tcp import ( + "context" + binaryserialization "github.com/apache/iggy/foreign/go/binary_serialization" iggcon "github.com/apache/iggy/foreign/go/contracts" "github.com/apache/iggy/foreign/go/internal/command" ) -func (c *IggyTcpClient) GetStats() (*iggcon.Stats, error) { - buffer, err := c.do(&command.GetStats{}) +func (c *IggyTcpClient) GetStats(ctx context.Context) (*iggcon.Stats, error) { + buffer, err := c.do(ctx, &command.GetStats{}) if err != nil { return nil, err } @@ -35,7 +37,7 @@ func (c *IggyTcpClient) GetStats() (*iggcon.Stats, error) { return &stats.Stats, err } -func (c *IggyTcpClient) Ping() error { - _, err := c.do(&command.Ping{}) +func (c *IggyTcpClient) Ping(ctx context.Context) error { + _, err := c.do(ctx, &command.Ping{}) return err } diff --git a/foreign/go/contracts/client.go b/foreign/go/contracts/client.go index f3b6dac655..7223497b68 100644 --- a/foreign/go/contracts/client.go +++ b/foreign/go/contracts/client.go @@ -17,6 +17,8 @@ package iggcon +import "context" + type Client interface { // Close closes the client and releases all the resources. Close() error @@ -26,39 +28,40 @@ type Client interface { // GetClusterMetadata get the metadata of the cluster including node information, roles, and status. // Authentication is required. - GetClusterMetadata() (*ClusterMetadata, error) + GetClusterMetadata(ctx context.Context) (*ClusterMetadata, error) // GetStream get the info about a specific stream by unique ID or name. // Authentication is required, and the permission to read the streams. - GetStream(streamId Identifier) (*StreamDetails, error) + GetStream(ctx context.Context, streamId Identifier) (*StreamDetails, error) // GetStreams get the info about all the streams. // Authentication is required, and the permission to read the streams. - GetStreams() ([]Stream, error) + GetStreams(ctx context.Context) ([]Stream, error) // CreateStream create a new stream. // Authentication is required, and the permission to manage the streams. - CreateStream(name string) (*StreamDetails, error) + CreateStream(ctx context.Context, name string) (*StreamDetails, error) // UpdateStream update a stream by unique ID or name. // Authentication is required, and the permission to manage the streams. - UpdateStream(streamId Identifier, name string) error + UpdateStream(ctx context.Context, streamId Identifier, name string) error // DeleteStream delete a stream by unique ID or name. // Authentication is required, and the permission to manage the streams. - DeleteStream(id Identifier) error + DeleteStream(ctx context.Context, id Identifier) error // GetTopic Get the info about a specific topic by unique ID or name. // Authentication is required, and the permission to read the topics. - GetTopic(streamId, topicId Identifier) (*TopicDetails, error) + GetTopic(ctx context.Context, streamId, topicId Identifier) (*TopicDetails, error) // GetTopics get the info about all the topics. // Authentication is required, and the permission to read the topics. - GetTopics(streamId Identifier) ([]Topic, error) + GetTopics(ctx context.Context, streamId Identifier) ([]Topic, error) // CreateTopic create a new topic. // Authentication is required, and the permission to manage the topics. CreateTopic( + ctx context.Context, streamId Identifier, name string, partitionsCount uint32, @@ -71,6 +74,7 @@ type Client interface { // UpdateTopic update a topic by unique ID or name. // Authentication is required, and the permission to manage the topics. UpdateTopic( + ctx context.Context, streamId Identifier, topicId Identifier, name string, @@ -82,11 +86,12 @@ type Client interface { // DeleteTopic delete a topic by unique ID or name. // Authentication is required, and the permission to manage the topics. - DeleteTopic(streamId, topicId Identifier) error + DeleteTopic(ctx context.Context, streamId, topicId Identifier) error // SendMessages sends messages using specified partitioning strategy to the given stream and topic by unique IDs or names. // Authentication is required, and the permission to send the messages. SendMessages( + ctx context.Context, streamId Identifier, topicId Identifier, partitioning Partitioning, @@ -96,6 +101,7 @@ type Client interface { // PollMessages poll given amount of messages using the specified consumer and strategy from the specified stream and topic by unique IDs or names. // Authentication is required, and the permission to poll the messages. PollMessages( + ctx context.Context, streamId Identifier, topicId Identifier, consumer Consumer, @@ -108,6 +114,7 @@ type Client interface { // StoreConsumerOffset store the consumer offset for a specific consumer or consumer group for the given stream and topic by unique IDs or names. // Authentication is required, and the permission to poll the messages. StoreConsumerOffset( + ctx context.Context, consumer Consumer, streamId Identifier, topicId Identifier, @@ -118,6 +125,7 @@ type Client interface { // GetConsumerOffset get the consumer offset for a specific consumer or consumer group for the given stream and topic by unique IDs or names. // Authentication is required, and the permission to poll the messages. GetConsumerOffset( + ctx context.Context, consumer Consumer, streamId Identifier, topicId Identifier, @@ -126,11 +134,12 @@ type Client interface { // GetConsumerGroups get the info about all the consumer groups for the given stream and topic by unique IDs or names. // Authentication is required, and the permission to read the streams or topics. - GetConsumerGroups(streamId Identifier, topicId Identifier) ([]ConsumerGroup, error) + GetConsumerGroups(ctx context.Context, streamId Identifier, topicId Identifier) ([]ConsumerGroup, error) // DeleteConsumerOffset delete the consumer offset for a specific consumer or consumer group for the given stream and topic by unique IDs or names. // Authentication is required, and the permission to poll the messages. DeleteConsumerOffset( + ctx context.Context, consumer Consumer, streamId Identifier, topicId Identifier, @@ -140,6 +149,7 @@ type Client interface { // GetConsumerGroup get the info about a specific consumer group by unique ID or name for the given stream and topic by unique IDs or names. // Authentication is required, and the permission to read the streams or topics. GetConsumerGroup( + ctx context.Context, streamId Identifier, topicId Identifier, groupId Identifier, @@ -148,6 +158,7 @@ type Client interface { // CreateConsumerGroup create a new consumer group for the given stream and topic by unique IDs or names. // Authentication is required, and the permission to manage the streams or topics. CreateConsumerGroup( + ctx context.Context, streamId Identifier, topicId Identifier, name string, @@ -156,6 +167,7 @@ type Client interface { // DeleteConsumerGroup delete a consumer group by unique ID or name for the given stream and topic by unique IDs or names. // Authentication is required, and the permission to manage the streams or topics. DeleteConsumerGroup( + ctx context.Context, streamId Identifier, topicId Identifier, groupId Identifier, @@ -164,6 +176,7 @@ type Client interface { // JoinConsumerGroup join a consumer group by unique ID or name for the given stream and topic by unique IDs or names. // Authentication is required, and the permission to read the streams or topics. JoinConsumerGroup( + ctx context.Context, streamId Identifier, topicId Identifier, groupId Identifier, @@ -172,6 +185,7 @@ type Client interface { // LeaveConsumerGroup leave a consumer group by unique ID or name for the given stream and topic by unique IDs or names. // Authentication is required, and the permission to read the streams or topics. LeaveConsumerGroup( + ctx context.Context, streamId Identifier, topicId Identifier, groupId Identifier, @@ -181,6 +195,7 @@ type Client interface { // For example, given a topic with 3 partitions, if you create 2 partitions, the topic will have 5 partitions (from 1 to 5). // Authentication is required, and the permission to manage the partitions. CreatePartitions( + ctx context.Context, streamId Identifier, topicId Identifier, partitionsCount uint32, @@ -190,6 +205,7 @@ type Client interface { // For example, given a topic with 5 partitions, if you delete 2 partitions, the topic will have 3 partitions left (from 1 to 3). // Authentication is required, and the permission to manage the partitions. DeletePartitions( + ctx context.Context, streamId Identifier, topicId Identifier, partitionsCount uint32, @@ -197,15 +213,16 @@ type Client interface { // GetUser get the info about a specific user by unique ID or username. // Authentication is required, and the permission to read the users, unless the provided user ID is the same as the authenticated user. - GetUser(identifier Identifier) (*UserInfoDetails, error) + GetUser(ctx context.Context, identifier Identifier) (*UserInfoDetails, error) // GetUsers get the info about all the users. // Authentication is required, and the permission to read the users. - GetUsers() ([]UserInfo, error) + GetUsers(ctx context.Context) ([]UserInfo, error) // CreateUser create a new user. // Authentication is required, and the permission to manage the users. CreateUser( + ctx context.Context, username string, password string, status UserStatus, @@ -215,6 +232,7 @@ type Client interface { // UpdateUser update a user by unique ID or username. // Authentication is required, and the permission to manage the users. UpdateUser( + ctx context.Context, userID Identifier, username *string, status *UserStatus, @@ -222,11 +240,12 @@ type Client interface { // UpdatePermissions update the permissions of a user by unique ID or username. // Authentication is required, and the permission to manage the users. - UpdatePermissions(userID Identifier, permissions *Permissions) error + UpdatePermissions(ctx context.Context, userID Identifier, permissions *Permissions) error // ChangePassword change the password of a user by unique ID or username. // Authentication is required, and the permission to manage the users, unless the provided user ID is the same as the authenticated user. ChangePassword( + ctx context.Context, userID Identifier, currentPassword string, newPassword string, @@ -234,38 +253,38 @@ type Client interface { // DeleteUser delete a user by unique ID or username. // Authentication is required, and the permission to manage the users. - DeleteUser(identifier Identifier) error + DeleteUser(ctx context.Context, identifier Identifier) error // CreatePersonalAccessToken create a new personal access token for the currently authenticated user. - CreatePersonalAccessToken(name string, expiry uint32) (*RawPersonalAccessToken, error) + CreatePersonalAccessToken(ctx context.Context, name string, expiry uint32) (*RawPersonalAccessToken, error) // DeletePersonalAccessToken delete a personal access token of the currently authenticated user by unique token name. - DeletePersonalAccessToken(name string) error + DeletePersonalAccessToken(ctx context.Context, name string) error // GetPersonalAccessTokens get the info about all the personal access tokens of the currently authenticated user. - GetPersonalAccessTokens() ([]PersonalAccessTokenInfo, error) + GetPersonalAccessTokens(ctx context.Context) ([]PersonalAccessTokenInfo, error) // LoginWithPersonalAccessToken login the user with the provided personal access token. - LoginWithPersonalAccessToken(token string) (*IdentityInfo, error) + LoginWithPersonalAccessToken(ctx context.Context, token string) (*IdentityInfo, error) // LoginUser login a user by username and password. - LoginUser(username string, password string) (*IdentityInfo, error) + LoginUser(ctx context.Context, username string, password string) (*IdentityInfo, error) // LogoutUser logout the currently authenticated user. - LogoutUser() error + LogoutUser(ctx context.Context) error // GetStats get the stats of the system such as PID, memory usage, streams count etc. // Authentication is required, and the permission to read the server info. - GetStats() (*Stats, error) + GetStats(ctx context.Context) (*Stats, error) // Ping the server to check if it's alive. - Ping() error + Ping(ctx context.Context) error // GetClients get the info about all the currently connected clients (not to be confused with the users). // Authentication is required, and the permission to read the server info. - GetClients() ([]ClientInfo, error) + GetClients(ctx context.Context) ([]ClientInfo, error) // GetClient get the info about a specific client by unique ID (not to be confused with the user). // Authentication is required, and the permission to read the server info. - GetClient(clientId uint32) (*ClientInfoDetails, error) + GetClient(ctx context.Context, clientId uint32) (*ClientInfoDetails, error) } diff --git a/foreign/go/internal/util/leader_aware.go b/foreign/go/internal/util/leader_aware.go index efa525154c..a38c692282 100644 --- a/foreign/go/internal/util/leader_aware.go +++ b/foreign/go/internal/util/leader_aware.go @@ -18,6 +18,7 @@ package util import ( + "context" "fmt" "log" "net" @@ -29,10 +30,10 @@ import ( // CheckAndRedirectToLeader queries the client for cluster metadata and returns // an address to redirect to (empty string means no redirection needed). -func CheckAndRedirectToLeader(c iggcon.Client, currentAddress string, transport iggcon.Protocol) (string, error) { +func CheckAndRedirectToLeader(ctx context.Context, c iggcon.Client, currentAddress string, transport iggcon.Protocol) (string, error) { log.Println("Checking cluster metadata for leader detection") - meta, err := c.GetClusterMetadata() + meta, err := c.GetClusterMetadata(ctx) if err != nil { log.Printf("Failed to get cluster metadata: %v, connection will continue on server node %s\n", err, currentAddress) return "", nil diff --git a/foreign/go/samples/consumer/consumer.go b/foreign/go/samples/consumer/consumer.go index ae97820350..d0621aa428 100644 --- a/foreign/go/samples/consumer/consumer.go +++ b/foreign/go/samples/consumer/consumer.go @@ -18,6 +18,7 @@ package main import ( + "context" "encoding/base64" "encoding/json" "fmt" @@ -47,7 +48,7 @@ func main() { if err != nil { panic(err) } - _, err = cli.LoginUser("iggy", "iggy") + _, err = cli.LoginUser(context.Background(), "iggy", "iggy") if err != nil { panic("COULD NOT LOG IN") } @@ -63,8 +64,8 @@ func main() { func EnsureInfrastructureIsInitialized(cli iggcon.Client) error { streamIdentifier, _ := iggcon.NewIdentifier(DefaultStreamId) - if _, streamErr := cli.GetStream(streamIdentifier); streamErr != nil { - _, streamErr = cli.CreateStream("Test Producer Stream") + if _, streamErr := cli.GetStream(context.Background(), streamIdentifier); streamErr != nil { + _, streamErr = cli.CreateStream(context.Background(), "Test Producer Stream") if streamErr != nil { panic(streamErr) @@ -76,8 +77,9 @@ func EnsureInfrastructureIsInitialized(cli iggcon.Client) error { fmt.Printf("Stream with ID: %d exists.\n", DefaultStreamId) topicIdentifier, _ := iggcon.NewIdentifier(TopicId) - if _, topicErr := cli.GetTopic(streamIdentifier, topicIdentifier); topicErr != nil { + if _, topicErr := cli.GetTopic(context.Background(), streamIdentifier, topicIdentifier); topicErr != nil { _, topicErr = cli.CreateTopic( + context.Background(), streamIdentifier, "Test Topic From Producer Sample", 12, @@ -107,6 +109,7 @@ func ConsumeMessages(cli iggcon.Client) error { consumerIdentifier, _ := iggcon.NewIdentifier(ConsumerId) partitionId := uint32(Partition) messagesWrapper, err := cli.PollMessages( + context.Background(), streamIdentifier, topicIdentifier, iggcon.NewSingleConsumer(consumerIdentifier), diff --git a/foreign/go/samples/producer/producer.go b/foreign/go/samples/producer/producer.go index 5163099661..0b5c2cae4a 100644 --- a/foreign/go/samples/producer/producer.go +++ b/foreign/go/samples/producer/producer.go @@ -18,6 +18,7 @@ package main import ( + "context" "fmt" "time" @@ -46,7 +47,7 @@ func main() { if err != nil { panic(err) } - _, err = cli.LoginUser("iggy", "iggy") + _, err = cli.LoginUser(context.Background(), "iggy", "iggy") if err != nil { panic("COULD NOT LOG IN") } @@ -62,8 +63,8 @@ func main() { func EnsureInfrastructureIsInitialized(cli iggcon.Client) error { streamIdentifier, _ := iggcon.NewIdentifier(StreamId) - if _, streamErr := cli.GetStream(streamIdentifier); streamErr != nil { - _, streamErr = cli.CreateStream("Test Producer Stream") + if _, streamErr := cli.GetStream(context.Background(), streamIdentifier); streamErr != nil { + _, streamErr = cli.CreateStream(context.Background(), "Test Producer Stream") fmt.Println(StreamId) @@ -77,8 +78,9 @@ func EnsureInfrastructureIsInitialized(cli iggcon.Client) error { fmt.Printf("Stream with ID: %d exists.\n", StreamId) topicIdentifier, _ := iggcon.NewIdentifier(TopicId) - if _, topicErr := cli.GetTopic(streamIdentifier, topicIdentifier); topicErr != nil { + if _, topicErr := cli.GetTopic(context.Background(), streamIdentifier, topicIdentifier); topicErr != nil { _, topicErr = cli.CreateTopic( + context.Background(), streamIdentifier, "Test Topic From Producer Sample", 12, @@ -126,6 +128,7 @@ func PublishMessages(messageStream iggcon.Client) error { streamIdentifier, _ := iggcon.NewIdentifier(StreamId) topicIdentifier, _ := iggcon.NewIdentifier(TopicId) err := messageStream.SendMessages( + context.Background(), streamIdentifier, topicIdentifier, iggcon.PartitionId(Partition), diff --git a/foreign/go/tests/tls_test.go b/foreign/go/tests/tls_test.go index c3525c9ddf..64adcc166c 100644 --- a/foreign/go/tests/tls_test.go +++ b/foreign/go/tests/tls_test.go @@ -164,7 +164,7 @@ func TestTCPTLSConnection_WithCA_Success(t *testing.T) { require.NoError(t, err, "Failed to create TLS client") defer func() { _ = cli.Close() }() - _, err = cli.LoginUser(defaultUsername, defaultPassword) + _, err = cli.LoginUser(context.Background(), defaultUsername, defaultPassword) require.NoError(t, err, "Login should succeed over TLS") } @@ -189,7 +189,7 @@ func TestTCPTLSConnection_WithoutTLS_Failure(t *testing.T) { if err == nil && cli != nil { defer func() { _ = cli.Close() }() - _, err = cli.LoginUser(defaultUsername, defaultPassword) + _, err = cli.LoginUser(context.Background(), defaultUsername, defaultPassword) } assert.Error(t, err, "Connection/login should fail when TLS is required but not used") @@ -220,21 +220,22 @@ func TestTCPTLSConnection_MessageFlow_Success(t *testing.T) { require.NoError(t, err, "Failed to create TLS client") defer func() { _ = cli.Close() }() - _, err = cli.LoginUser(defaultUsername, defaultPassword) + _, err = cli.LoginUser(context.Background(), defaultUsername, defaultPassword) require.NoError(t, err, "Login should succeed") streamName := "tls-test-stream" - _, err = cli.CreateStream(streamName) + _, err = cli.CreateStream(context.Background(), streamName) require.NoError(t, err, "Failed to create stream") defer func() { streamIdentifier, _ := iggcon.NewIdentifier(streamName) - _ = cli.DeleteStream(streamIdentifier) + _ = cli.DeleteStream(context.Background(), streamIdentifier) }() topicName := "tls-test-topic" streamIdentifier, _ := iggcon.NewIdentifier(streamName) _, err = cli.CreateTopic( + context.Background(), streamIdentifier, topicName, 1, // partitionCount @@ -256,12 +257,13 @@ func TestTCPTLSConnection_MessageFlow_Success(t *testing.T) { topicIdentifier, _ := iggcon.NewIdentifier(topicName) partitionID := uint32(0) partitioning := iggcon.PartitionId(partitionID) - err = cli.SendMessages(streamIdentifier, topicIdentifier, partitioning, messages) + err = cli.SendMessages(context.Background(), streamIdentifier, topicIdentifier, partitioning, messages) require.NoError(t, err, "Failed to send messages over TLS") consumer := iggcon.DefaultConsumer() offset := uint64(0) pollMessages, err := cli.PollMessages( + context.Background(), streamIdentifier, topicIdentifier, consumer, From 49b546152f37f5bd30dfac279f7494ca89a07571 Mon Sep 17 00:00:00 2001 From: chengxi Date: Tue, 17 Mar 2026 21:35:12 -0400 Subject: [PATCH 02/13] refactor(go): return error when context is nil. --- foreign/go/client/tcp/tcp_core.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/foreign/go/client/tcp/tcp_core.go b/foreign/go/client/tcp/tcp_core.go index fef53f449a..9151869ae0 100644 --- a/foreign/go/client/tcp/tcp_core.go +++ b/foreign/go/client/tcp/tcp_core.go @@ -22,6 +22,7 @@ import ( "crypto/tls" "crypto/x509" "encoding/binary" + "errors" "fmt" "net" "os" @@ -266,6 +267,9 @@ func (c *IggyTcpClient) do(ctx context.Context, cmd command.Command) ([]byte, er } func (c *IggyTcpClient) sendAndFetchResponse(ctx context.Context, message []byte, command command.Code) ([]byte, error) { + if ctx == nil { + return nil, errors.New("nil context") + } if err := ctx.Err(); err != nil { return nil, err } From 3283ffcee1fadba6e9b16853f251fafd6b42c868 Mon Sep 17 00:00:00 2001 From: chengxi Date: Wed, 18 Mar 2026 01:11:41 -0400 Subject: [PATCH 03/13] feat(go): implement test for context --- foreign/go/client/tcp/tcp_core_test.go | 128 +++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 foreign/go/client/tcp/tcp_core_test.go diff --git a/foreign/go/client/tcp/tcp_core_test.go b/foreign/go/client/tcp/tcp_core_test.go new file mode 100644 index 0000000000..f139c718fb --- /dev/null +++ b/foreign/go/client/tcp/tcp_core_test.go @@ -0,0 +1,128 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package tcp + +import ( + "context" + "errors" + "net" + "strings" + "testing" + "time" + + iggcon "github.com/apache/iggy/foreign/go/contracts" + "github.com/apache/iggy/foreign/go/internal/command" +) + +// newTestClient creates an IggyTcpClient backed by an in-memory net.Pipe connection. +// Returns the client and the server-side end of the pipe; caller must close the server conn. +func newTestClient(t *testing.T) (*IggyTcpClient, net.Conn) { + t.Helper() + serverConn, clientConn := net.Pipe() + c := &IggyTcpClient{ + conn: clientConn, + state: iggcon.StateConnected, + } + t.Cleanup(func() { + err := clientConn.Close() + if err != nil { + t.Errorf("error closing client connection: %v", err) + } + }) + t.Cleanup(func() { + err := serverConn.Close() + if err != nil { + t.Errorf("error closing server connection: %v", err) + } + }) + return c, serverConn +} + +func TestSendAndFetchResponse_NilContext(t *testing.T) { + c, _ := newTestClient(t) + _, err := c.sendAndFetchResponse(context.Background(), []byte{}, command.Code(0)) + if err == nil { + t.Fatal("expected error, got nil") + } + if !strings.Contains(err.Error(), "nil context") { + t.Errorf("got %q, want it to contain %q", err.Error(), "nil context") + } +} + +func TestSendAndFetchResponse_ContextErrors(t *testing.T) { + canceledCtx, cancel := context.WithCancel(context.Background()) + cancel() + + expiredCtx, expiredCancel := context.WithDeadline(context.Background(), time.Now().Add(-time.Second)) + defer expiredCancel() + + tests := []struct { + name string + ctx context.Context + wantErr error + }{ + { + name: "canceled", + ctx: canceledCtx, + wantErr: context.Canceled, + }, + { + name: "expired", + ctx: expiredCtx, + wantErr: context.DeadlineExceeded, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c, _ := newTestClient(t) + + // server does not respond, but it doesn't matter. + // ctx.Err() should fire before any I/O is attempted. + _, err := c.sendAndFetchResponse(tt.ctx, []byte{}, command.Code(0)) + if err == nil { + t.Fatal("expected error, got nil") + } + if !errors.Is(err, tt.wantErr) { + t.Errorf("got %v, want %v", err, tt.wantErr) + } + }) + } +} + +func TestSendAndFetchResponse_DeadlineTimeout(t *testing.T) { + c, _ := newTestClient(t) + + // server intentionally does not read or write, causing the client to block + // until the context deadline fires and SetDeadline triggers a timeout. + ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) + defer cancel() + + _, err := c.sendAndFetchResponse(ctx, []byte{}, command.Code(0)) + if err == nil { + t.Fatal("expected timeout error, got nil") + } + + var netErr net.Error + if !errors.As(err, &netErr) { + t.Fatalf("expected a net.Error, got %T: %v", err, err) + } + if !netErr.Timeout() { + t.Errorf("expected timeout error, got %v", err) + } +} From 8b12d4e1684c29bf2a655c412c61d70564d8b17e Mon Sep 17 00:00:00 2001 From: chengxi Date: Wed, 18 Mar 2026 01:48:00 -0400 Subject: [PATCH 04/13] refactor(go): update examples/samples --- examples/go/getting-started/consumer/main.go | 21 ++++++++++++++----- examples/go/getting-started/producer/main.go | 17 +++++++++------ foreign/go/samples/consumer/consumer.go | 22 +++++++++++--------- foreign/go/samples/producer/producer.go | 22 +++++++++++--------- 4 files changed, 51 insertions(+), 31 deletions(-) diff --git a/examples/go/getting-started/consumer/main.go b/examples/go/getting-started/consumer/main.go index b758b28779..83c05fe776 100644 --- a/examples/go/getting-started/consumer/main.go +++ b/examples/go/getting-started/consumer/main.go @@ -18,6 +18,7 @@ package main import ( + "context" "flag" "log" "net" @@ -52,18 +53,19 @@ func main() { }() log.Printf("Connecting to server at %s", serverAddr) - _, err = cli.LoginUser(common.DefaultRootUsername, common.DefaultRootPassword) + ctx := context.Background() + _, err = cli.LoginUser(ctx, common.DefaultRootUsername, common.DefaultRootPassword) if err != nil { log.Fatal(err) } - err = consumeMessages(cli) + err = consumeMessages(ctx, cli) if err != nil { log.Fatal(err) } } -func consumeMessages(client iggcon.Client) error { +func consumeMessages(ctx context.Context, client iggcon.Client) error { interval := 500 * time.Millisecond log.Printf( "Messages will be consumed from stream: %d, topic: %d, partition: %d with interval %s.", @@ -84,6 +86,7 @@ func consumeMessages(client iggcon.Client) error { topicIdentifier, _ := iggcon.NewIdentifier(TopicID) pollMessages, err := client. PollMessages( + ctx, streamIdentifier, topicIdentifier, consumer, @@ -98,7 +101,11 @@ func consumeMessages(client iggcon.Client) error { if len(pollMessages.Messages) == 0 { log.Println("No messages found.") - time.Sleep(interval) + select { + case <-ctx.Done(): + return nil + case <-time.After(interval): + } continue } @@ -110,7 +117,11 @@ func consumeMessages(client iggcon.Client) error { } } consumedBatches++ - time.Sleep(interval) + select { + case <-ctx.Done(): + return nil + case <-time.After(interval): + } } } diff --git a/examples/go/getting-started/producer/main.go b/examples/go/getting-started/producer/main.go index 5aa2a2807e..023d0cf076 100644 --- a/examples/go/getting-started/producer/main.go +++ b/examples/go/getting-started/producer/main.go @@ -18,6 +18,7 @@ package main import ( + "context" "flag" "fmt" "log" @@ -54,23 +55,26 @@ func main() { }() log.Printf("Connecting to server at %s", serverAddr) - if _, err := cli.LoginUser(common.DefaultRootUsername, common.DefaultRootPassword); err != nil { + + ctx := context.Background() + if _, err := cli.LoginUser(ctx, common.DefaultRootUsername, common.DefaultRootPassword); err != nil { log.Fatalf("Login failed: %v", err) } - initSystem(cli) - if err := produceMessages(cli); err != nil { + initSystem(ctx, cli) + if err := produceMessages(ctx, cli); err != nil { log.Fatalf("Producing messages failed: %v", err) } } -func initSystem(client iggcon.Client) { - if _, err := client.CreateStream("sample-stream"); err != nil { +func initSystem(ctx context.Context, client iggcon.Client) { + if _, err := client.CreateStream(ctx, "sample-stream"); err != nil { log.Printf("WARN: Stream already exists or error: %v", err) } log.Println("Stream was created.") streamIdentifier, _ := iggcon.NewIdentifier(StreamId) if _, err := client.CreateTopic( + ctx, streamIdentifier, "sample-topic", 1, @@ -83,7 +87,7 @@ func initSystem(client iggcon.Client) { log.Println("Topic was created.") } -func produceMessages(client iggcon.Client) error { +func produceMessages(ctx context.Context, client iggcon.Client) error { interval := 500 * time.Millisecond log.Printf( "Messages will be sent to stream: %d, topic: %d, partition: %d with interval %s.", @@ -115,6 +119,7 @@ func produceMessages(client iggcon.Client) error { streamIdentifier, _ := iggcon.NewIdentifier(StreamId) topicIdentifier, _ := iggcon.NewIdentifier(TopicId) if err := client.SendMessages( + ctx, streamIdentifier, topicIdentifier, partitioning, diff --git a/foreign/go/samples/consumer/consumer.go b/foreign/go/samples/consumer/consumer.go index d0621aa428..58f532947d 100644 --- a/foreign/go/samples/consumer/consumer.go +++ b/foreign/go/samples/consumer/consumer.go @@ -40,6 +40,8 @@ const ( ) func main() { + ctx := context.Background() + cli, err := client.NewIggyClient( client.WithTcp( tcp.WithServerAddress("127.0.0.1:8090"), @@ -48,24 +50,24 @@ func main() { if err != nil { panic(err) } - _, err = cli.LoginUser(context.Background(), "iggy", "iggy") + _, err = cli.LoginUser(ctx, "iggy", "iggy") if err != nil { panic("COULD NOT LOG IN") } - if err = EnsureInfrastructureIsInitialized(cli); err != nil { + if err = EnsureInfrastructureIsInitialized(ctx, cli); err != nil { panic(err) } - if err = ConsumeMessages(cli); err != nil { + if err = ConsumeMessages(ctx, cli); err != nil { panic(err) } } -func EnsureInfrastructureIsInitialized(cli iggcon.Client) error { +func EnsureInfrastructureIsInitialized(ctx context.Context, cli iggcon.Client) error { streamIdentifier, _ := iggcon.NewIdentifier(DefaultStreamId) - if _, streamErr := cli.GetStream(context.Background(), streamIdentifier); streamErr != nil { - _, streamErr = cli.CreateStream(context.Background(), "Test Producer Stream") + if _, streamErr := cli.GetStream(ctx, streamIdentifier); streamErr != nil { + _, streamErr = cli.CreateStream(ctx, "Test Producer Stream") if streamErr != nil { panic(streamErr) @@ -77,9 +79,9 @@ func EnsureInfrastructureIsInitialized(cli iggcon.Client) error { fmt.Printf("Stream with ID: %d exists.\n", DefaultStreamId) topicIdentifier, _ := iggcon.NewIdentifier(TopicId) - if _, topicErr := cli.GetTopic(context.Background(), streamIdentifier, topicIdentifier); topicErr != nil { + if _, topicErr := cli.GetTopic(ctx, streamIdentifier, topicIdentifier); topicErr != nil { _, topicErr = cli.CreateTopic( - context.Background(), + ctx, streamIdentifier, "Test Topic From Producer Sample", 12, @@ -100,7 +102,7 @@ func EnsureInfrastructureIsInitialized(cli iggcon.Client) error { return nil } -func ConsumeMessages(cli iggcon.Client) error { +func ConsumeMessages(ctx context.Context, cli iggcon.Client) error { fmt.Printf("Messages will be polled from stream '%d', topic '%d', partition '%d' with interval %d ms.\n", DefaultStreamId, TopicId, Partition, Interval) for { @@ -109,7 +111,7 @@ func ConsumeMessages(cli iggcon.Client) error { consumerIdentifier, _ := iggcon.NewIdentifier(ConsumerId) partitionId := uint32(Partition) messagesWrapper, err := cli.PollMessages( - context.Background(), + ctx, streamIdentifier, topicIdentifier, iggcon.NewSingleConsumer(consumerIdentifier), diff --git a/foreign/go/samples/producer/producer.go b/foreign/go/samples/producer/producer.go index 0b5c2cae4a..f42217a6f1 100644 --- a/foreign/go/samples/producer/producer.go +++ b/foreign/go/samples/producer/producer.go @@ -39,6 +39,8 @@ const ( ) func main() { + ctx := context.Background() + cli, err := client.NewIggyClient( client.WithTcp( tcp.WithServerAddress("127.0.0.1:8090"), @@ -47,24 +49,24 @@ func main() { if err != nil { panic(err) } - _, err = cli.LoginUser(context.Background(), "iggy", "iggy") + _, err = cli.LoginUser(ctx, "iggy", "iggy") if err != nil { panic("COULD NOT LOG IN") } - if err = EnsureInfrastructureIsInitialized(cli); err != nil { + if err = EnsureInfrastructureIsInitialized(ctx, cli); err != nil { panic(err) } - if err = PublishMessages(cli); err != nil { + if err = PublishMessages(ctx, cli); err != nil { panic(err) } } -func EnsureInfrastructureIsInitialized(cli iggcon.Client) error { +func EnsureInfrastructureIsInitialized(ctx context.Context, cli iggcon.Client) error { streamIdentifier, _ := iggcon.NewIdentifier(StreamId) - if _, streamErr := cli.GetStream(context.Background(), streamIdentifier); streamErr != nil { - _, streamErr = cli.CreateStream(context.Background(), "Test Producer Stream") + if _, streamErr := cli.GetStream(ctx, streamIdentifier); streamErr != nil { + _, streamErr = cli.CreateStream(ctx, "Test Producer Stream") fmt.Println(StreamId) @@ -78,9 +80,9 @@ func EnsureInfrastructureIsInitialized(cli iggcon.Client) error { fmt.Printf("Stream with ID: %d exists.\n", StreamId) topicIdentifier, _ := iggcon.NewIdentifier(TopicId) - if _, topicErr := cli.GetTopic(context.Background(), streamIdentifier, topicIdentifier); topicErr != nil { + if _, topicErr := cli.GetTopic(ctx, streamIdentifier, topicIdentifier); topicErr != nil { _, topicErr = cli.CreateTopic( - context.Background(), + ctx, streamIdentifier, "Test Topic From Producer Sample", 12, @@ -101,7 +103,7 @@ func EnsureInfrastructureIsInitialized(cli iggcon.Client) error { return nil } -func PublishMessages(messageStream iggcon.Client) error { +func PublishMessages(ctx context.Context, messageStream iggcon.Client) error { fmt.Printf("Messages will be sent to stream '%d', topic '%d', partition '%d' with interval %d ms.\n", StreamId, TopicId, Partition, Interval) messageGenerator := NewMessageGenerator() @@ -128,7 +130,7 @@ func PublishMessages(messageStream iggcon.Client) error { streamIdentifier, _ := iggcon.NewIdentifier(StreamId) topicIdentifier, _ := iggcon.NewIdentifier(TopicId) err := messageStream.SendMessages( - context.Background(), + ctx, streamIdentifier, topicIdentifier, iggcon.PartitionId(Partition), From 0b5e30792cd26e9a129972f0b660b04ada62a3c0 Mon Sep 17 00:00:00 2001 From: chengxi Date: Wed, 18 Mar 2026 01:55:15 -0400 Subject: [PATCH 05/13] fix(go): nil context test should provide nil context --- foreign/go/client/tcp/tcp_core_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/foreign/go/client/tcp/tcp_core_test.go b/foreign/go/client/tcp/tcp_core_test.go index f139c718fb..34b3bec75b 100644 --- a/foreign/go/client/tcp/tcp_core_test.go +++ b/foreign/go/client/tcp/tcp_core_test.go @@ -55,7 +55,7 @@ func newTestClient(t *testing.T) (*IggyTcpClient, net.Conn) { func TestSendAndFetchResponse_NilContext(t *testing.T) { c, _ := newTestClient(t) - _, err := c.sendAndFetchResponse(context.Background(), []byte{}, command.Code(0)) + _, err := c.sendAndFetchResponse(nil, []byte{}, command.Code(0)) if err == nil { t.Fatal("expected error, got nil") } From db4e4f5483f7d6a7e91c8dd6def502a5729fdc2c Mon Sep 17 00:00:00 2001 From: chengxi Date: Wed, 18 Mar 2026 01:56:32 -0400 Subject: [PATCH 06/13] chore(go): add no lint comment --- foreign/go/client/tcp/tcp_core_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/foreign/go/client/tcp/tcp_core_test.go b/foreign/go/client/tcp/tcp_core_test.go index 34b3bec75b..ad12f3c431 100644 --- a/foreign/go/client/tcp/tcp_core_test.go +++ b/foreign/go/client/tcp/tcp_core_test.go @@ -55,7 +55,7 @@ func newTestClient(t *testing.T) (*IggyTcpClient, net.Conn) { func TestSendAndFetchResponse_NilContext(t *testing.T) { c, _ := newTestClient(t) - _, err := c.sendAndFetchResponse(nil, []byte{}, command.Code(0)) + _, err := c.sendAndFetchResponse(nil, []byte{}, command.Code(0)) //nolint:staticcheck if err == nil { t.Fatal("expected error, got nil") } From d75bb99bf8f38f747270514a2fd8aa4732c2a44a Mon Sep 17 00:00:00 2001 From: chengxi Date: Wed, 18 Mar 2026 11:00:10 -0400 Subject: [PATCH 07/13] chore(go): solve the inconsistent context usage in verifyLeaderInMetadata and verifyClientConnection --- bdd/go/tests/leader_redirection.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/bdd/go/tests/leader_redirection.go b/bdd/go/tests/leader_redirection.go index aac2379433..6da3ff7342 100644 --- a/bdd/go/tests/leader_redirection.go +++ b/bdd/go/tests/leader_redirection.go @@ -121,8 +121,8 @@ func serverTypeFromPort(port uint16) string { } } -func verifyLeaderInMetadata(client iggcon.Client) (*iggcon.ClusterNode, error) { - metadata, err := client.GetClusterMetadata(context.Background()) +func verifyLeaderInMetadata(ctx context.Context, client iggcon.Client) (*iggcon.ClusterNode, error) { + metadata, err := client.GetClusterMetadata(ctx) if err != nil { if isClusteringUnavailable(err) { // Clustering not enabled, this is OK @@ -142,7 +142,7 @@ func verifyLeaderInMetadata(client iggcon.Client) (*iggcon.ClusterNode, error) { return nil, nil } -func verifyClientConnection(client iggcon.Client, expectedPort uint16) (string, error) { +func verifyClientConnection(ctx context.Context, client iggcon.Client, expectedPort uint16) (string, error) { connInfo := client.GetConnectionInfo() expectedSuffix := fmt.Sprintf(":%d", expectedPort) @@ -155,7 +155,7 @@ func verifyClientConnection(client iggcon.Client, expectedPort uint16) (string, } // Verify client can communicate - if err := client.Ping(context.Background()); err != nil { + if err := client.Ping(ctx); err != nil { return "", fmt.Errorf("client cannot ping server: %w", err) } @@ -368,10 +368,10 @@ func (s leaderSteps) thenVerifyClientPort(ctx context.Context, expectedPort uint return errors.New("client should exist") } - if _, err := verifyClientConnection(cli, expectedPort); err != nil { + if _, err := verifyClientConnection(ctx, cli, expectedPort); err != nil { return err } - leader, err := verifyLeaderInMetadata(cli) + leader, err := verifyLeaderInMetadata(ctx, cli) if err != nil { return err } @@ -390,10 +390,10 @@ func (s leaderSteps) thenVerifyNamedClientPort(ctx context.Context, clientName s return fmt.Errorf("client %s should exist", clientName) } - if _, err := verifyClientConnection(cli, port); err != nil { + if _, err := verifyClientConnection(ctx, cli, port); err != nil { return fmt.Errorf("client %s connection should succeed: %w", clientName, err) } - leader, err := verifyLeaderInMetadata(cli) + leader, err := verifyLeaderInMetadata(ctx, cli) if err != nil { return err } @@ -421,7 +421,7 @@ func (s leaderSteps) thenConnectionRemains(ctx context.Context, port uint16) err return errors.New("client should exist") } - if _, err := verifyClientConnection(cli, port); err != nil { + if _, err := verifyClientConnection(ctx, cli, port); err != nil { return fmt.Errorf("should remain on original port: %w", err) } if c.TestState.RedirectionOccurred { @@ -467,11 +467,11 @@ func (s leaderSteps) thenBothUseSameServer(ctx context.Context) error { return errors.New("client B should be able to ping") } - leaderA, err := verifyLeaderInMetadata(a) + leaderA, err := verifyLeaderInMetadata(ctx, a) if err != nil { return err } - leaderB, err := verifyLeaderInMetadata(b) + leaderB, err := verifyLeaderInMetadata(ctx, b) if err != nil { return err } From 1de13785e2fe6b05e8abf33f985ddee7aa197cc2 Mon Sep 17 00:00:00 2001 From: chengxi Date: Wed, 18 Mar 2026 11:34:17 -0400 Subject: [PATCH 08/13] fix(go): re-checking ctx.Err() immediately after acquiring the lock. --- foreign/go/client/tcp/tcp_core.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/foreign/go/client/tcp/tcp_core.go b/foreign/go/client/tcp/tcp_core.go index 9151869ae0..d68ea03250 100644 --- a/foreign/go/client/tcp/tcp_core.go +++ b/foreign/go/client/tcp/tcp_core.go @@ -277,6 +277,10 @@ func (c *IggyTcpClient) sendAndFetchResponse(ctx context.Context, message []byte c.mtx.Lock() defer c.mtx.Unlock() + if err := ctx.Err(); err != nil { + return nil, err + } + if deadline, ok := ctx.Deadline(); ok { if err := c.conn.SetDeadline(deadline); err != nil { return nil, err From ab17bfafe704b42449427aba1cefbdf1b81b594b Mon Sep 17 00:00:00 2001 From: chengxi Date: Wed, 18 Mar 2026 12:01:02 -0400 Subject: [PATCH 09/13] fix(go): use WaitGroup to wait Ping to finish, avoid producing a spurious error log. --- foreign/go/client/iggy_client.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/foreign/go/client/iggy_client.go b/foreign/go/client/iggy_client.go index 07959bd778..d9ba95047a 100644 --- a/foreign/go/client/iggy_client.go +++ b/foreign/go/client/iggy_client.go @@ -21,6 +21,7 @@ import ( "context" "fmt" "log" + "sync" "time" "github.com/apache/iggy/foreign/go/client/tcp" @@ -55,6 +56,7 @@ func WithTcp(tcpOpts ...tcp.Option) Option { type IggyClient struct { iggcon.Client cancel context.CancelFunc + wg sync.WaitGroup } // NewIggyClient create the IggyClient instance. @@ -79,8 +81,14 @@ func NewIggyClient(options ...Option) (iggcon.Client, error) { } ctx, cancel := context.WithCancel(context.Background()) heartbeatInterval := opts.heartbeatInterval + ic := &IggyClient{ + Client: cli, + cancel: cancel, + } if heartbeatInterval > 0 { + ic.wg.Add(1) go func() { + defer ic.wg.Done() ticker := time.NewTicker(heartbeatInterval) defer ticker.Stop() for { @@ -95,13 +103,11 @@ func NewIggyClient(options ...Option) (iggcon.Client, error) { } }() } - return &IggyClient{ - Client: cli, - cancel: cancel, - }, nil + return ic, nil } func (ic *IggyClient) Close() error { ic.cancel() + ic.wg.Wait() return ic.Client.Close() } From e0f7a2efc2406a4007800bc3c9f1c352905a68c2 Mon Sep 17 00:00:00 2001 From: chengxi Date: Wed, 18 Mar 2026 12:01:41 -0400 Subject: [PATCH 10/13] fix(go): add timeout for Ping --- foreign/go/client/iggy_client.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/foreign/go/client/iggy_client.go b/foreign/go/client/iggy_client.go index d9ba95047a..c6a498cfc5 100644 --- a/foreign/go/client/iggy_client.go +++ b/foreign/go/client/iggy_client.go @@ -96,9 +96,11 @@ func NewIggyClient(options ...Option) (iggcon.Client, error) { case <-ctx.Done(): return case <-ticker.C: - if err := cli.Ping(context.Background()); err != nil { + pingCtx, pingCancel := context.WithTimeout(ctx, heartbeatInterval/2) + if err := cli.Ping(pingCtx); err != nil { log.Printf("[WARN] heartbeat failed: %v", err) } + pingCancel() } } }() From 8ba63f5ec44550193b30b16057a78d9015121673 Mon Sep 17 00:00:00 2001 From: chengxi Date: Tue, 31 Mar 2026 14:06:35 -0400 Subject: [PATCH 11/13] fix(go): stop in-flight connection when expire/canceled, disconnect when I/O error happens. --- foreign/go/client/tcp/tcp_core.go | 35 ++++++++++++++++++++++---- foreign/go/client/tcp/tcp_core_test.go | 27 ++++++++++++++++++++ 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/foreign/go/client/tcp/tcp_core.go b/foreign/go/client/tcp/tcp_core.go index d68ea03250..94be9f0aea 100644 --- a/foreign/go/client/tcp/tcp_core.go +++ b/foreign/go/client/tcp/tcp_core.go @@ -281,44 +281,69 @@ func (c *IggyTcpClient) sendAndFetchResponse(ctx context.Context, message []byte return nil, err } - if deadline, ok := ctx.Deadline(); ok { - if err := c.conn.SetDeadline(deadline); err != nil { - return nil, err - } - defer func() { _ = c.conn.SetDeadline(time.Time{}) }() + // deadlineMu makes sure that the deadline won't be set to now by the + // AfterFunc callback right after we call SetDeadline(time.Time{}) to + // clear the deadline after the operation is done. + var deadlineMu sync.Mutex + + stop := context.AfterFunc(ctx, func() { + deadlineMu.Lock() + defer deadlineMu.Unlock() + _ = c.conn.SetDeadline(time.Now()) + }) + defer stop() + + clearDeadline := func() { + stop() + deadlineMu.Lock() + defer deadlineMu.Unlock() + _ = c.conn.SetDeadline(time.Time{}) } payload := createPayload(message, command) if _, err := c.write(payload); err != nil { + c.invalidateConnLocked() return nil, err } readBytes, buffer, err := c.read(ResponseInitialBytesLength) if err != nil { + c.invalidateConnLocked() return nil, fmt.Errorf("failed to read response for TCP request: %w", err) } if readBytes != ResponseInitialBytesLength { + c.invalidateConnLocked() return nil, fmt.Errorf("received an invalid or empty response: %w", ierror.EmptyResponse{}) } if status := ierror.Code(binary.LittleEndian.Uint32(buffer[0:4])); status != 0 { + clearDeadline() return nil, ierror.FromCode(status) } length := int(binary.LittleEndian.Uint32(buffer[4:])) if length <= 1 { + clearDeadline() return []byte{}, nil } _, buffer, err = c.read(length) if err != nil { + c.invalidateConnLocked() return nil, err } + clearDeadline() return buffer, nil } +// invalidateConnLocked closes the connection and marks it as disconnected +func (c *IggyTcpClient) invalidateConnLocked() { + _ = c.conn.Close() + c.state = iggcon.StateDisconnected +} + func createPayload(message []byte, command command.Code) []byte { messageLength := len(message) + 4 messageBytes := make([]byte, RequestInitialBytesLength+messageLength) diff --git a/foreign/go/client/tcp/tcp_core_test.go b/foreign/go/client/tcp/tcp_core_test.go index ad12f3c431..6b47d56601 100644 --- a/foreign/go/client/tcp/tcp_core_test.go +++ b/foreign/go/client/tcp/tcp_core_test.go @@ -125,4 +125,31 @@ func TestSendAndFetchResponse_DeadlineTimeout(t *testing.T) { if !netErr.Timeout() { t.Errorf("expected timeout error, got %v", err) } + // After a timeout, the connection should be invalidated. + if c.state != iggcon.StateDisconnected { + t.Errorf("expected state %v, got %v", iggcon.StateDisconnected, c.state) + } +} + +func TestSendAndFetchResponse_CancelDuringIO(t *testing.T) { + c, _ := newTestClient(t) + + ctx, cancel := context.WithCancel(context.Background()) + + // Cancel the context after a short delay to unblock the I/O. + go func() { + time.Sleep(100 * time.Millisecond) + cancel() + }() + + // Server does not respond, so the client blocks until the context is cancelled. + _, err := c.sendAndFetchResponse(ctx, []byte{}, command.Code(0)) + if err == nil { + t.Fatal("expected error, got nil") + } + + // Connection should be invalidated after the I/O error. + if c.state != iggcon.StateDisconnected { + t.Errorf("expected state %v, got %v", iggcon.StateDisconnected, c.state) + } } From 4b094ecfce2fe98798c9c7c7f02eb2194402b27a Mon Sep 17 00:00:00 2001 From: chengxi Date: Tue, 31 Mar 2026 14:35:46 -0400 Subject: [PATCH 12/13] fix(go): separate context.Canceled/DeadlineExceeded from network error --- foreign/go/client/tcp/tcp_core.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/foreign/go/client/tcp/tcp_core.go b/foreign/go/client/tcp/tcp_core.go index 94be9f0aea..493973bb92 100644 --- a/foreign/go/client/tcp/tcp_core.go +++ b/foreign/go/client/tcp/tcp_core.go @@ -300,16 +300,26 @@ func (c *IggyTcpClient) sendAndFetchResponse(ctx context.Context, message []byte _ = c.conn.SetDeadline(time.Time{}) } + // ioError returns ctx.Err() when the context caused the I/O failure, + // so callers get a consistent context.Canceled / context.DeadlineExceeded + // instead of a low-level net timeout error. + ioError := func(err error) error { + if ctxErr := ctx.Err(); ctxErr != nil { + return ctxErr + } + return err + } + payload := createPayload(message, command) if _, err := c.write(payload); err != nil { c.invalidateConnLocked() - return nil, err + return nil, ioError(err) } readBytes, buffer, err := c.read(ResponseInitialBytesLength) if err != nil { c.invalidateConnLocked() - return nil, fmt.Errorf("failed to read response for TCP request: %w", err) + return nil, ioError(err) } if readBytes != ResponseInitialBytesLength { @@ -331,7 +341,7 @@ func (c *IggyTcpClient) sendAndFetchResponse(ctx context.Context, message []byte _, buffer, err = c.read(length) if err != nil { c.invalidateConnLocked() - return nil, err + return nil, ioError(err) } clearDeadline() From fcef70f58a72dbd82abe89fcdaf54cd55edf4aad Mon Sep 17 00:00:00 2001 From: chengxi Date: Tue, 31 Mar 2026 15:07:05 -0400 Subject: [PATCH 13/13] chore(go): improve test coverage. --- foreign/go/client/tcp/tcp_core_test.go | 94 ++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 6 deletions(-) diff --git a/foreign/go/client/tcp/tcp_core_test.go b/foreign/go/client/tcp/tcp_core_test.go index 6b47d56601..e79ee258d0 100644 --- a/foreign/go/client/tcp/tcp_core_test.go +++ b/foreign/go/client/tcp/tcp_core_test.go @@ -19,6 +19,7 @@ package tcp import ( "context" + "encoding/binary" "errors" "net" "strings" @@ -26,6 +27,7 @@ import ( "time" iggcon "github.com/apache/iggy/foreign/go/contracts" + ierror "github.com/apache/iggy/foreign/go/errors" "github.com/apache/iggy/foreign/go/internal/command" ) @@ -118,12 +120,8 @@ func TestSendAndFetchResponse_DeadlineTimeout(t *testing.T) { t.Fatal("expected timeout error, got nil") } - var netErr net.Error - if !errors.As(err, &netErr) { - t.Fatalf("expected a net.Error, got %T: %v", err, err) - } - if !netErr.Timeout() { - t.Errorf("expected timeout error, got %v", err) + if !errors.Is(err, context.DeadlineExceeded) { + t.Errorf("got %v, want context.DeadlineExceeded", err) } // After a timeout, the connection should be invalidated. if c.state != iggcon.StateDisconnected { @@ -148,8 +146,92 @@ func TestSendAndFetchResponse_CancelDuringIO(t *testing.T) { t.Fatal("expected error, got nil") } + if !errors.Is(err, context.Canceled) { + t.Errorf("got %v, want context.Canceled", err) + } // Connection should be invalidated after the I/O error. if c.state != iggcon.StateDisconnected { t.Errorf("expected state %v, got %v", iggcon.StateDisconnected, c.state) } } + +// serverRespond is a test helper that reads the full request from the pipe +// and writes back a response with the given status code and payload. +func serverRespond(t *testing.T, serverConn net.Conn, status uint32, payload []byte) { + t.Helper() + + var lengthBuf [RequestInitialBytesLength]byte + if _, err := serverConn.Read(lengthBuf[:]); err != nil { + t.Errorf("server: read request length: %v", err) + return + } + reqLen := int(binary.LittleEndian.Uint32(lengthBuf[:])) + discard := make([]byte, reqLen) + if _, err := serverConn.Read(discard); err != nil { + t.Errorf("server: read request body: %v", err) + return + } + + resp := make([]byte, 8+len(payload)) + binary.LittleEndian.PutUint32(resp[0:4], status) + binary.LittleEndian.PutUint32(resp[4:8], uint32(len(payload))) + copy(resp[8:], payload) + if _, err := serverConn.Write(resp); err != nil { + t.Errorf("server: write response: %v", err) + } +} + +func TestSendAndFetchResponse_ErrorStatus(t *testing.T) { + c, serverConn := newTestClient(t) + + go serverRespond(t, serverConn, uint32(ierror.UnauthenticatedCode), nil) + + _, err := c.sendAndFetchResponse(context.Background(), []byte{}, command.Code(0)) + if err == nil { + t.Fatal("expected error, got nil") + } + + // Should return the iggy error corresponding to the status code. + if !errors.Is(err, ierror.ErrUnauthenticated) { + t.Errorf("got %v, want %v", err, ierror.ErrUnauthenticated) + } + // Connection should remain healthy after an application-level error. + if c.state != iggcon.StateConnected { + t.Errorf("expected state %v, got %v", iggcon.StateConnected, c.state) + } +} + +func TestSendAndFetchResponse_SuccessEmptyBody(t *testing.T) { + c, serverConn := newTestClient(t) + + go serverRespond(t, serverConn, 0, nil) + + result, err := c.sendAndFetchResponse(context.Background(), []byte{}, command.Code(0)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(result) != 0 { + t.Errorf("expected empty result, got %d bytes", len(result)) + } + if c.state != iggcon.StateConnected { + t.Errorf("expected state %v, got %v", iggcon.StateConnected, c.state) + } +} + +func TestSendAndFetchResponse_SuccessWithBody(t *testing.T) { + c, serverConn := newTestClient(t) + + body := []byte("hello iggy") + go serverRespond(t, serverConn, 0, body) + + result, err := c.sendAndFetchResponse(context.Background(), []byte{}, command.Code(0)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if string(result) != string(body) { + t.Errorf("got %q, want %q", result, body) + } + if c.state != iggcon.StateConnected { + t.Errorf("expected state %v, got %v", iggcon.StateConnected, c.state) + } +}