diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b0931ea..daa3ab9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -53,9 +53,3 @@ jobs: files: ./coverage.out # optional fail_ci_if_error: false # optional (default = false) verbose: false # optional (default = false) -# - uses: go-semantic-release/action@v1 -# id: semver -# with: -# github-token: ${{ secrets.GITHUB_TOKEN }} -# allow-initial-development-versions: true -# force-bump-patch-version: true diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..ea279d9 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,74 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ main ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ main ] + schedule: + - cron: '28 20 * * 0' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'go' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://git.io/codeql-language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + # โ„น๏ธ Command-line programs to run using the OS shell. + # ๐Ÿ“š https://git.io/JvXDl + + # โœ๏ธ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 diff --git a/.golangci.yml b/.golangci.yml index ac428e1..b806ed1 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -39,6 +39,9 @@ issues: - revive linters-settings: + gosec: + excludes: + - G115 misspell: locale: US ignore-words: diff --git a/Earthfile b/Earthfile index dc33ce5..1d8fb42 100644 --- a/Earthfile +++ b/Earthfile @@ -1,8 +1,10 @@ VERSION 0.8 -FROM tochemey/docker-go:1.22.0-3.0.0 +FROM tochemey/docker-go:1.23.4-5.1.1 RUN go install github.com/ory/go-acc@latest +# install vektra/mockery +RUN go install github.com/vektra/mockery/v2@v2.52.1 protogen: # copy the proto files to generate @@ -92,6 +94,8 @@ mock: # generate the mocks RUN mockery --dir persistence --all --keeptree --exported=true --with-expecter=true --inpackage=true --disable-version-string=true --output ./mocks/persistence --case snake RUN mockery --dir offsetstore --name OffsetStore --keeptree --exported=true --with-expecter=true --inpackage=true --disable-version-string=true --output ./mocks/offsetstore --case snake + RUN mockery --dir . --name EventPublisher --keeptree --exported=true --with-expecter=true --inpackage=true --disable-version-string=true --output ./mocks/ego --case snake + RUN mockery --dir . --name StatePublisher --keeptree --exported=true --with-expecter=true --inpackage=true --disable-version-string=true --output ./mocks/ego --case snake SAVE ARTIFACT ./mocks mocks AS LOCAL mocks \ No newline at end of file diff --git a/behavior.go b/behavior.go index e433ee9..1894a9c 100644 --- a/behavior.go +++ b/behavior.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/durable_state_actor.go b/durable_state_actor.go index 5001d16..4491374 100644 --- a/durable_state_actor.go +++ b/durable_state_actor.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,8 +30,8 @@ import ( "math" "time" - "github.com/tochemey/goakt/v2/actors" - "github.com/tochemey/goakt/v2/goaktpb" + goakt "github.com/tochemey/goakt/v3/actor" + "github.com/tochemey/goakt/v3/goaktpb" "golang.org/x/sync/errgroup" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" @@ -55,11 +55,11 @@ type durableStateActor struct { currentVersion uint64 lastCommandTime time.Time eventsStream eventstream.Stream - actorSystem actors.ActorSystem + actorSystem goakt.ActorSystem } -// implements the actors.Actor interface -var _ actors.Actor = (*durableStateActor)(nil) +// implements the goakt.Actor interface +var _ goakt.Actor = (*durableStateActor)(nil) // newDurableStateActor creates an instance of actor provided the DurableStateBehavior func newDurableStateActor(behavior DurableStateBehavior, stateStore persistence.StateStore, eventsStream eventstream.Stream) *durableStateActor { @@ -81,7 +81,7 @@ func (entity *durableStateActor) PreStart(ctx context.Context) error { } // Receive processes any message dropped into the actor mailbox. -func (entity *durableStateActor) Receive(ctx *actors.ReceiveContext) { +func (entity *durableStateActor) Receive(ctx *goakt.ReceiveContext) { switch command := ctx.Message().(type) { case *goaktpb.PostStart: entity.actorSystem = ctx.ActorSystem() @@ -125,7 +125,7 @@ func (entity *durableStateActor) recoverFromStore(ctx context.Context) error { } // processCommand processes the incoming command -func (entity *durableStateActor) processCommand(receiveContext *actors.ReceiveContext, command Command) { +func (entity *durableStateActor) processCommand(receiveContext *goakt.ReceiveContext, command Command) { ctx := receiveContext.Context() newState, newVersion, err := entity.HandleCommand(ctx, command, entity.currentVersion, entity.currentState) if err != nil { @@ -153,7 +153,7 @@ func (entity *durableStateActor) processCommand(receiveContext *actors.ReceiveCo } // sendStateReply sends a state reply message -func (entity *durableStateActor) sendStateReply(ctx *actors.ReceiveContext) { +func (entity *durableStateActor) sendStateReply(ctx *goakt.ReceiveContext) { state, _ := anypb.New(entity.currentState) ctx.Response(&egopb.CommandReply{ Reply: &egopb.CommandReply_StateReply{ @@ -168,7 +168,7 @@ func (entity *durableStateActor) sendStateReply(ctx *actors.ReceiveContext) { } // sendErrorReply sends an error as a reply message -func (entity *durableStateActor) sendErrorReply(ctx *actors.ReceiveContext, err error) { +func (entity *durableStateActor) sendErrorReply(ctx *goakt.ReceiveContext, err error) { ctx.Response(&egopb.CommandReply{ Reply: &egopb.CommandReply_ErrorReply{ ErrorReply: &egopb.ErrorReply{ @@ -211,6 +211,8 @@ func (entity *durableStateActor) persistStateAndPublish(ctx context.Context) err shardNumber := entity.actorSystem.GetPartition(entity.ID()) topic := fmt.Sprintf(statesTopic, shardNumber) + entity.actorSystem.Logger().Debugf("publishing durableState to topic: %s", topic) + durableState := &egopb.DurableState{ PersistenceId: entity.ID(), VersionNumber: entity.currentVersion, diff --git a/durable_state_actor_test.go b/durable_state_actor_test.go index a3dfcb1..bd45994 100644 --- a/durable_state_actor_test.go +++ b/durable_state_actor_test.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -32,8 +32,8 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tochemey/goakt/v2/actors" - "github.com/tochemey/goakt/v2/log" + goakt "github.com/tochemey/goakt/v3/actor" + "github.com/tochemey/goakt/v3/log" "google.golang.org/protobuf/proto" "github.com/tochemey/ego/v3/egopb" @@ -47,10 +47,10 @@ func TestDurableStateBehavior(t *testing.T) { t.Run("with state reply", func(t *testing.T) { ctx := context.TODO() // create an actor system - actorSystem, err := actors.NewActorSystem("TestActorSystem", - actors.WithPassivationDisabled(), - actors.WithLogger(log.DiscardLogger), - actors.WithActorInitMaxRetries(3)) + actorSystem, err := goakt.NewActorSystem("TestActorSystem", + goakt.WithPassivationDisabled(), + goakt.WithLogger(log.DiscardLogger), + goakt.WithActorInitMaxRetries(3)) require.NoError(t, err) assert.NotNil(t, actorSystem) @@ -80,7 +80,7 @@ func TestDurableStateBehavior(t *testing.T) { command = &testpb.CreateAccount{AccountBalance: 500.00} // send the command to the actor - reply, err := actors.Ask(ctx, pid, command, 5*time.Second) + reply, err := goakt.Ask(ctx, pid, command, 5*time.Second) require.NoError(t, err) require.NotNil(t, reply) require.IsType(t, new(egopb.CommandReply), reply) @@ -107,7 +107,7 @@ func TestDurableStateBehavior(t *testing.T) { AccountId: persistenceID, Balance: 250, } - reply, err = actors.Ask(ctx, pid, command, 5*time.Second) + reply, err = goakt.Ask(ctx, pid, command, 5*time.Second) require.NoError(t, err) require.NotNil(t, reply) require.IsType(t, new(egopb.CommandReply), reply) @@ -143,10 +143,10 @@ func TestDurableStateBehavior(t *testing.T) { ctx := context.TODO() // create an actor system - actorSystem, err := actors.NewActorSystem("TestActorSystem", - actors.WithPassivationDisabled(), - actors.WithLogger(log.DiscardLogger), - actors.WithActorInitMaxRetries(3)) + actorSystem, err := goakt.NewActorSystem("TestActorSystem", + goakt.WithPassivationDisabled(), + goakt.WithLogger(log.DiscardLogger), + goakt.WithActorInitMaxRetries(3)) require.NoError(t, err) assert.NotNil(t, actorSystem) @@ -182,7 +182,7 @@ func TestDurableStateBehavior(t *testing.T) { command = &testpb.CreateAccount{AccountBalance: 500.00} // send the command to the actor - reply, err := actors.Ask(ctx, pid, command, time.Second) + reply, err := goakt.Ask(ctx, pid, command, time.Second) require.NoError(t, err) require.NotNil(t, reply) require.IsType(t, new(egopb.CommandReply), reply) @@ -209,7 +209,7 @@ func TestDurableStateBehavior(t *testing.T) { AccountId: "different-id", Balance: 250, } - reply, err = actors.Ask(ctx, pid, command, time.Second) + reply, err = goakt.Ask(ctx, pid, command, time.Second) require.NoError(t, err) require.NotNil(t, reply) require.IsType(t, new(egopb.CommandReply), reply) @@ -231,10 +231,10 @@ func TestDurableStateBehavior(t *testing.T) { }) t.Run("with state recovery from state store", func(t *testing.T) { ctx := context.TODO() - actorSystem, err := actors.NewActorSystem("TestActorSystem", - actors.WithPassivationDisabled(), - actors.WithLogger(log.DiscardLogger), - actors.WithActorInitMaxRetries(3), + actorSystem, err := goakt.NewActorSystem("TestActorSystem", + goakt.WithPassivationDisabled(), + goakt.WithLogger(log.DiscardLogger), + goakt.WithActorInitMaxRetries(3), ) require.NoError(t, err) assert.NotNil(t, actorSystem) @@ -271,7 +271,7 @@ func TestDurableStateBehavior(t *testing.T) { command = &testpb.CreateAccount{AccountBalance: 500.00} - reply, err := actors.Ask(ctx, pid, command, time.Second) + reply, err := goakt.Ask(ctx, pid, command, time.Second) require.NoError(t, err) require.NotNil(t, reply) require.IsType(t, new(egopb.CommandReply), reply) @@ -298,7 +298,7 @@ func TestDurableStateBehavior(t *testing.T) { AccountId: persistenceID, Balance: 250, } - reply, err = actors.Ask(ctx, pid, command, time.Second) + reply, err = goakt.Ask(ctx, pid, command, time.Second) require.NoError(t, err) require.NotNil(t, reply) require.IsType(t, new(egopb.CommandReply), reply) @@ -331,7 +331,7 @@ func TestDurableStateBehavior(t *testing.T) { // fetch the current state command = &egopb.GetStateCommand{} - reply, err = actors.Ask(ctx, pid, command, time.Second) + reply, err = goakt.Ask(ctx, pid, command, time.Second) require.NoError(t, err) require.NotNil(t, reply) require.IsType(t, new(egopb.CommandReply), reply) diff --git a/egopb/ego.pb.go b/egopb/ego.pb.go index b9ffdac..557a987 100644 --- a/egopb/ego.pb.go +++ b/egopb/ego.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.31.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: ego/v3/ego.proto @@ -23,10 +23,7 @@ const ( // Event defines the event that needs to be persisted onto the events store type Event struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - + state protoimpl.MessageState `protogen:"open.v1"` // Specifies the persistence unique identifier PersistenceId string `protobuf:"bytes,1,opt,name=persistence_id,json=persistenceId,proto3" json:"persistence_id,omitempty"` // Specifies the sequence number @@ -40,16 +37,16 @@ type Event struct { // Specifies the timestamp Timestamp int64 `protobuf:"varint,6,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // Specifies the shard number - Shard uint64 `protobuf:"varint,7,opt,name=shard,proto3" json:"shard,omitempty"` + Shard uint64 `protobuf:"varint,7,opt,name=shard,proto3" json:"shard,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *Event) Reset() { *x = Event{} - if protoimpl.UnsafeEnabled { - mi := &file_ego_v3_ego_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_ego_v3_ego_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Event) String() string { @@ -60,7 +57,7 @@ func (*Event) ProtoMessage() {} func (x *Event) ProtoReflect() protoreflect.Message { mi := &file_ego_v3_ego_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -127,26 +124,23 @@ func (x *Event) GetShard() uint64 { // CommandReply specifies the reply to a command sent to // the aggregate type CommandReply struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - + state protoimpl.MessageState `protogen:"open.v1"` // the actual command reply // - // Types that are assignable to Reply: + // Types that are valid to be assigned to Reply: // // *CommandReply_StateReply // *CommandReply_ErrorReply - Reply isCommandReply_Reply `protobuf_oneof:"reply"` + Reply isCommandReply_Reply `protobuf_oneof:"reply"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *CommandReply) Reset() { *x = CommandReply{} - if protoimpl.UnsafeEnabled { - mi := &file_ego_v3_ego_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_ego_v3_ego_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *CommandReply) String() string { @@ -157,7 +151,7 @@ func (*CommandReply) ProtoMessage() {} func (x *CommandReply) ProtoReflect() protoreflect.Message { mi := &file_ego_v3_ego_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -172,23 +166,27 @@ func (*CommandReply) Descriptor() ([]byte, []int) { return file_ego_v3_ego_proto_rawDescGZIP(), []int{1} } -func (m *CommandReply) GetReply() isCommandReply_Reply { - if m != nil { - return m.Reply +func (x *CommandReply) GetReply() isCommandReply_Reply { + if x != nil { + return x.Reply } return nil } func (x *CommandReply) GetStateReply() *StateReply { - if x, ok := x.GetReply().(*CommandReply_StateReply); ok { - return x.StateReply + if x != nil { + if x, ok := x.Reply.(*CommandReply_StateReply); ok { + return x.StateReply + } } return nil } func (x *CommandReply) GetErrorReply() *ErrorReply { - if x, ok := x.GetReply().(*CommandReply_ErrorReply); ok { - return x.ErrorReply + if x != nil { + if x, ok := x.Reply.(*CommandReply_ErrorReply); ok { + return x.ErrorReply + } } return nil } @@ -214,10 +212,7 @@ func (*CommandReply_ErrorReply) isCommandReply_Reply() {} // StateReply is a command reply. // This is reply is used when there is a no-op type StateReply struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - + state protoimpl.MessageState `protogen:"open.v1"` // Specifies the persistence unique identifier PersistenceId string `protobuf:"bytes,1,opt,name=persistence_id,json=persistenceId,proto3" json:"persistence_id,omitempty"` // the entity state @@ -225,16 +220,16 @@ type StateReply struct { // Specifies the sequence number SequenceNumber uint64 `protobuf:"varint,3,opt,name=sequence_number,json=sequenceNumber,proto3" json:"sequence_number,omitempty"` // Specifies the timestamp - Timestamp int64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Timestamp int64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *StateReply) Reset() { *x = StateReply{} - if protoimpl.UnsafeEnabled { - mi := &file_ego_v3_ego_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_ego_v3_ego_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *StateReply) String() string { @@ -245,7 +240,7 @@ func (*StateReply) ProtoMessage() {} func (x *StateReply) ProtoReflect() protoreflect.Message { mi := &file_ego_v3_ego_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -291,21 +286,18 @@ func (x *StateReply) GetTimestamp() int64 { // ErrorReply is used when a command processing has // failed. type ErrorReply struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - + state protoimpl.MessageState `protogen:"open.v1"` // Specifies the error message - Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *ErrorReply) Reset() { *x = ErrorReply{} - if protoimpl.UnsafeEnabled { - mi := &file_ego_v3_ego_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_ego_v3_ego_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ErrorReply) String() string { @@ -316,7 +308,7 @@ func (*ErrorReply) ProtoMessage() {} func (x *ErrorReply) ProtoReflect() protoreflect.Message { mi := &file_ego_v3_ego_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -340,18 +332,16 @@ func (x *ErrorReply) GetMessage() string { // NoReply is used when a command does not need a reply type NoReply struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *NoReply) Reset() { *x = NoReply{} - if protoimpl.UnsafeEnabled { - mi := &file_ego_v3_ego_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_ego_v3_ego_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *NoReply) String() string { @@ -362,7 +352,7 @@ func (*NoReply) ProtoMessage() {} func (x *NoReply) ProtoReflect() protoreflect.Message { mi := &file_ego_v3_ego_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -380,18 +370,16 @@ func (*NoReply) Descriptor() ([]byte, []int) { // GetStateCommand tells the Aggregate // to reply with its latest state type GetStateCommand struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *GetStateCommand) Reset() { *x = GetStateCommand{} - if protoimpl.UnsafeEnabled { - mi := &file_ego_v3_ego_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_ego_v3_ego_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *GetStateCommand) String() string { @@ -402,7 +390,7 @@ func (*GetStateCommand) ProtoMessage() {} func (x *GetStateCommand) ProtoReflect() protoreflect.Message { mi := &file_ego_v3_ego_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -419,10 +407,7 @@ func (*GetStateCommand) Descriptor() ([]byte, []int) { // Offset defines the projection offset type Offset struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - + state protoimpl.MessageState `protogen:"open.v1"` // Specifies the shard number ShardNumber uint64 `protobuf:"varint,1,opt,name=shard_number,json=shardNumber,proto3" json:"shard_number,omitempty"` // Specifies the projection name. @@ -430,16 +415,16 @@ type Offset struct { // Specifies the value Value int64 `protobuf:"varint,3,opt,name=value,proto3" json:"value,omitempty"` // Specifies the timestamp - Timestamp int64 `protobuf:"varint,5,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Timestamp int64 `protobuf:"varint,5,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *Offset) Reset() { *x = Offset{} - if protoimpl.UnsafeEnabled { - mi := &file_ego_v3_ego_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_ego_v3_ego_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Offset) String() string { @@ -450,7 +435,7 @@ func (*Offset) ProtoMessage() {} func (x *Offset) ProtoReflect() protoreflect.Message { mi := &file_ego_v3_ego_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -495,23 +480,20 @@ func (x *Offset) GetTimestamp() int64 { // ProjectionId defines the projection id type ProjectionId struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - + state protoimpl.MessageState `protogen:"open.v1"` // Specifies the projection name ProjectionName string `protobuf:"bytes,1,opt,name=projection_name,json=projectionName,proto3" json:"projection_name,omitempty"` // Specifies the shard number - ShardNumber uint64 `protobuf:"varint,2,opt,name=shard_number,json=shardNumber,proto3" json:"shard_number,omitempty"` + ShardNumber uint64 `protobuf:"varint,2,opt,name=shard_number,json=shardNumber,proto3" json:"shard_number,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *ProjectionId) Reset() { *x = ProjectionId{} - if protoimpl.UnsafeEnabled { - mi := &file_ego_v3_ego_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_ego_v3_ego_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ProjectionId) String() string { @@ -522,7 +504,7 @@ func (*ProjectionId) ProtoMessage() {} func (x *ProjectionId) ProtoReflect() protoreflect.Message { mi := &file_ego_v3_ego_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -554,10 +536,7 @@ func (x *ProjectionId) GetShardNumber() uint64 { // DurableState defines the durable state behavior // actor type DurableState struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - + state protoimpl.MessageState `protogen:"open.v1"` // Specifies the persistence unique identifier PersistenceId string `protobuf:"bytes,1,opt,name=persistence_id,json=persistenceId,proto3" json:"persistence_id,omitempty"` // Specifies the version number @@ -567,16 +546,16 @@ type DurableState struct { // Specifies the timestamp Timestamp int64 `protobuf:"varint,5,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // Specifies the shard number - Shard uint64 `protobuf:"varint,6,opt,name=shard,proto3" json:"shard,omitempty"` + Shard uint64 `protobuf:"varint,6,opt,name=shard,proto3" json:"shard,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *DurableState) Reset() { *x = DurableState{} - if protoimpl.UnsafeEnabled { - mi := &file_ego_v3_ego_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_ego_v3_ego_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *DurableState) String() string { @@ -587,7 +566,7 @@ func (*DurableState) ProtoMessage() {} func (x *DurableState) ProtoReflect() protoreflect.Message { mi := &file_ego_v3_ego_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -735,7 +714,7 @@ func file_ego_v3_ego_proto_rawDescGZIP() []byte { } var file_ego_v3_ego_proto_msgTypes = make([]protoimpl.MessageInfo, 9) -var file_ego_v3_ego_proto_goTypes = []interface{}{ +var file_ego_v3_ego_proto_goTypes = []any{ (*Event)(nil), // 0: ego.v1.Event (*CommandReply)(nil), // 1: ego.v1.CommandReply (*StateReply)(nil), // 2: ego.v1.StateReply @@ -766,117 +745,7 @@ func file_ego_v3_ego_proto_init() { if File_ego_v3_ego_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_ego_v3_ego_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Event); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_ego_v3_ego_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CommandReply); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_ego_v3_ego_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StateReply); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_ego_v3_ego_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ErrorReply); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_ego_v3_ego_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NoReply); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_ego_v3_ego_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetStateCommand); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_ego_v3_ego_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Offset); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_ego_v3_ego_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProjectionId); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_ego_v3_ego_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DurableState); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - file_ego_v3_ego_proto_msgTypes[1].OneofWrappers = []interface{}{ + file_ego_v3_ego_proto_msgTypes[1].OneofWrappers = []any{ (*CommandReply_StateReply)(nil), (*CommandReply_ErrorReply)(nil), } diff --git a/engine.go b/engine.go index dded161..97d37db 100644 --- a/engine.go +++ b/engine.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -32,16 +32,17 @@ import ( "sync" "time" - "github.com/tochemey/goakt/v2/actors" - "github.com/tochemey/goakt/v2/address" - "github.com/tochemey/goakt/v2/discovery" - "github.com/tochemey/goakt/v2/log" - "github.com/tochemey/goakt/v2/remote" + goakt "github.com/tochemey/goakt/v3/actor" + "github.com/tochemey/goakt/v3/address" + "github.com/tochemey/goakt/v3/discovery" + "github.com/tochemey/goakt/v3/log" + "github.com/tochemey/goakt/v3/remote" "go.uber.org/atomic" "google.golang.org/protobuf/proto" "github.com/tochemey/ego/v3/egopb" "github.com/tochemey/ego/v3/eventstream" + "github.com/tochemey/ego/v3/internal/syncmap" "github.com/tochemey/ego/v3/offsetstore" "github.com/tochemey/ego/v3/persistence" "github.com/tochemey/ego/v3/projection" @@ -58,25 +59,44 @@ var ( ErrDurableStateStoreRequired = errors.New("durable state store is required") ) +// Done is a signal that an operation has completed +type Done struct{} + +type eventsStream struct { + publisher EventPublisher + subscriber eventstream.Subscriber + done chan Done +} + +type statesStream struct { + publisher StatePublisher + subscriber eventstream.Subscriber + done chan Done +} + // Engine represents the engine that empowers the various entities type Engine struct { name string // name is the application name eventsStore persistence.EventsStore // eventsStore is the events store stateStore persistence.StateStore // stateStore is the durable state store enableCluster *atomic.Bool // enableCluster enable/disable cluster mode - actorSystem actors.ActorSystem // actorSystem is the underlying actor system + actorSystem goakt.ActorSystem // actorSystem is the underlying actor system logger log.Logger // logger is the logging engine to use discoveryProvider discovery.Provider // discoveryProvider is the discovery provider for clustering partitionsCount uint64 // partitionsCount specifies the number of partitions started atomic.Bool - hostName string + bindAddr string peersPort int - gossipPort int + discoveryPort int remotingPort int minimumPeersQuorum uint16 eventStream eventstream.Stream mutex *sync.Mutex - remoting *actors.Remoting + remoting *goakt.Remoting + tls *TLS + + eventsStreams *syncmap.Map[string, *eventsStream] + statesStreams *syncmap.Map[string, *statesStream] } // NewEngine creates and initializes a new instance of the eGo engine. @@ -100,13 +120,20 @@ func NewEngine(name string, eventsStore persistence.EventsStore, opts ...Option) logger: log.New(log.ErrorLevel, os.Stderr), eventStream: eventstream.New(), mutex: &sync.Mutex{}, - remoting: actors.NewRemoting(), + bindAddr: "0.0.0.0", + remoting: goakt.NewRemoting(), + eventsStreams: syncmap.New[string, *eventsStream](), + statesStreams: syncmap.New[string, *statesStream](), } for _, opt := range opts { opt.Apply(e) } + if e.tls != nil { + e.remoting = goakt.NewRemoting(goakt.WithRemotingTLS(e.tls.ClientTLS)) + } + e.started.Store(false) return e } @@ -123,15 +150,22 @@ func NewEngine(name string, eventsStore persistence.EventsStore, opts ...Option) // Returns: // - An error if the engine fails to start due to misconfiguration or system issues; otherwise, nil. func (engine *Engine) Start(ctx context.Context) error { - opts := []actors.Option{ - actors.WithLogger(engine.logger), - actors.WithPassivationDisabled(), - actors.WithActorInitMaxRetries(1), + opts := []goakt.Option{ + goakt.WithLogger(engine.logger), + goakt.WithPassivationDisabled(), + goakt.WithActorInitMaxRetries(1), + } + + if engine.tls != nil { + opts = append(opts, goakt.WithTLS(&goakt.TLSInfo{ + ClientTLS: engine.tls.ClientTLS, + ServerTLS: engine.tls.ServerTLS, + })) } if engine.enableCluster.Load() { - if engine.hostName == "" { - engine.hostName, _ = os.Hostname() + if engine.bindAddr == "" { + engine.bindAddr, _ = os.Hostname() } replicaCount := 1 @@ -139,10 +173,10 @@ func (engine *Engine) Start(ctx context.Context) error { replicaCount = 2 } - clusterConfig := actors. + clusterConfig := goakt. NewClusterConfig(). WithDiscovery(engine.discoveryProvider). - WithDiscoveryPort(engine.gossipPort). + WithDiscoveryPort(engine.discoveryPort). WithPeersPort(engine.peersPort). WithMinimumPeersQuorum(uint32(engine.minimumPeersQuorum)). WithReplicaCount(uint32(replicaCount)). @@ -153,12 +187,12 @@ func (engine *Engine) Start(ctx context.Context) error { ) opts = append(opts, - actors.WithCluster(clusterConfig), - actors.WithRemote(remote.NewConfig(engine.hostName, engine.remotingPort))) + goakt.WithCluster(clusterConfig), + goakt.WithRemote(remote.NewConfig(engine.bindAddr, engine.remotingPort))) } var err error - engine.actorSystem, err = actors.NewActorSystem(engine.name, opts...) + engine.actorSystem, err = goakt.NewActorSystem(engine.name, opts...) if err != nil { return fmt.Errorf("failed to create the ego actor system: %w", err) } @@ -200,8 +234,8 @@ func (engine *Engine) AddProjection(ctx context.Context, name string, handler pr actorSystem := engine.actorSystem engine.mutex.Unlock() - // projections are long-lived actors - if _, err := actorSystem.Spawn(ctx, name, actor, actors.WithLongLived()); err != nil { + // projections are long-lived goakt + if _, err := actorSystem.Spawn(ctx, name, actor, goakt.WithLongLived()); err != nil { return fmt.Errorf("failed to register the projection=(%s): %w", name, err) } @@ -277,7 +311,30 @@ func (engine *Engine) IsProjectionRunning(ctx context.Context, name string) (boo // - An error if the shutdown process encounters issues; otherwise, nil. func (engine *Engine) Stop(ctx context.Context) error { engine.started.Store(false) + + // shutdown all event eventsStreams + eventsStreams := engine.eventsStreams.Values() + for _, stream := range eventsStreams { + // signal the publisher to stop + stream.done <- Done{} + stream.subscriber.Shutdown() + if err := stream.publisher.Close(ctx); err != nil { + return err + } + } + + statesStreams := engine.statesStreams.Values() + for _, stream := range statesStreams { + stream.done <- Done{} + stream.subscriber.Shutdown() + if err := stream.publisher.Close(ctx); err != nil { + return err + } + } + engine.eventStream.Close() + engine.eventsStreams.Reset() + engine.statesStreams.Reset() return engine.actorSystem.Stop(ctx) } @@ -286,7 +343,14 @@ func (engine *Engine) Started() bool { return engine.started.Load() } -// Subscribe creates an events subscriber +// Subscribe creates an events subscriber. +// +// This function initializes a new subscriber for the event stream managed by the eGo engine. The subscriber +// will receive events from the topics specified by the engine's configuration. +// +// Returns: +// - An eventstream.Subscriber instance that can be used to receive events. +// - An error if the engine has not started or if there is an issue creating the subscriber. func (engine *Engine) Subscribe() (eventstream.Subscriber, error) { if !engine.Started() { return nil, ErrEngineNotStarted @@ -297,11 +361,10 @@ func (engine *Engine) Subscribe() (eventstream.Subscriber, error) { engine.mutex.Unlock() subscriber := eventStream.AddSubscriber() - for i := 0; i < int(engine.partitionsCount); i++ { - topic := fmt.Sprintf(eventsTopic, i) - engine.eventStream.Subscribe(subscriber, topic) - topic = fmt.Sprintf(statesTopic, i) - engine.eventStream.Subscribe(subscriber, topic) + eventTopics := generateTopics(eventsTopic, engine.partitionsCount) + stateTopics := generateTopics(statesTopic, engine.partitionsCount) + for _, topic := range append(eventTopics, stateTopics...) { + eventStream.Subscribe(subscriber, topic) } return subscriber, nil @@ -341,13 +404,13 @@ func (engine *Engine) Entity(ctx context.Context, behavior EventSourcedBehavior, engine.mutex.Unlock() config := newSpawnConfig(opts...) - var sOptions []actors.SpawnOption + var sOptions []goakt.SpawnOption switch { case config.passivateAfter > 0: - sOptions = append(sOptions, actors.WithPassivateAfter(config.passivateAfter)) + sOptions = append(sOptions, goakt.WithPassivateAfter(config.passivateAfter)) default: - sOptions = append(sOptions, actors.WithLongLived()) + sOptions = append(sOptions, goakt.WithLongLived()) } _, err := actorSystem.Spawn(ctx, @@ -398,13 +461,13 @@ func (engine *Engine) DurableStateEntity(ctx context.Context, behavior DurableSt } config := newSpawnConfig(opts...) - var sOptions []actors.SpawnOption + var sOptions []goakt.SpawnOption switch { case config.passivateAfter > 0: - sOptions = append(sOptions, actors.WithPassivateAfter(config.passivateAfter)) + sOptions = append(sOptions, goakt.WithPassivateAfter(config.passivateAfter)) default: - sOptions = append(sOptions, actors.WithLongLived()) + sOptions = append(sOptions, goakt.WithLongLived()) } _, err := actorSystem.Spawn(ctx, @@ -462,7 +525,7 @@ func (engine *Engine) SendCommand(ctx context.Context, entityID string, cmd Comm var reply proto.Message switch { case pid != nil: - reply, err = actors.Ask(ctx, pid, cmd, timeout) + reply, err = goakt.Ask(ctx, pid, cmd, timeout) case addr != nil: res, err := engine.remoting.RemoteAsk(ctx, address.NoSender(), addr, cmd, timeout) if err == nil { @@ -483,6 +546,94 @@ func (engine *Engine) SendCommand(ctx context.Context, entityID string, cmd Comm return nil, 0, ErrCommandReplyUnmarshalling } +// AddEventPublishers registers one or more event publishers with the eGo engine. +// This function subscribes the publishers to the event stream, allowing them to receive events. +// +// Note: Event publishers are responsible for publishing events to external systems. They need to be added to the engine before processing any events. +// +// Parameters: +// - publishers: A list of event publishers to be added to the engine. +// +// Returns an error if the engine has not started. +func (engine *Engine) AddEventPublishers(publishers ...EventPublisher) error { + if !engine.Started() { + return ErrEngineNotStarted + } + + engine.mutex.Lock() + defer engine.mutex.Unlock() + + for _, publisher := range publishers { + subscriber := engine.eventStream.AddSubscriber() + topics := generateTopics(eventsTopic, engine.partitionsCount) + for _, topic := range topics { + engine.logger.Debugf("%s subscribing to topic: %s", publisher.ID(), topic) + engine.eventStream.Subscribe(subscriber, topic) + } + + // create an instance of the event subscriber + eventSubscriber := &eventsStream{ + publisher: publisher, + subscriber: subscriber, + done: make(chan Done, 1), + } + + // add the event publisher to the engine + engine.eventsStreams.Set(publisher.ID(), eventSubscriber) + + // start the event publisher + engine.logger.Infof("starting %s events publisher....", publisher.ID()) + go engine.sendEvent(eventSubscriber) + } + + return nil +} + +// AddStatePublishers registers one or more state publishers with the eGo engine. +// This function subscribes the publishers to the event stream, allowing them to receive state changes. +// +// Note: State publishers are responsible for publishing durable state changes to external systems. +// They need to be added to the engine before processing any durable state. +// +// Parameters: +// - publishers: A list of state publishers to be added to the engine. +// +// Returns an error if the engine has not started. +func (engine *Engine) AddStatePublishers(publishers ...StatePublisher) error { + if !engine.Started() { + return ErrEngineNotStarted + } + + engine.mutex.Lock() + defer engine.mutex.Unlock() + + for _, publisher := range publishers { + subscriber := engine.eventStream.AddSubscriber() + topics := generateTopics(statesTopic, engine.partitionsCount) + + for _, topic := range topics { + engine.logger.Debugf("%s subscribing to topic: %s", publisher.ID(), topic) + engine.eventStream.Subscribe(subscriber, topic) + } + + // create an instance of the state subscriber + stateSubscriber := &statesStream{ + publisher: publisher, + subscriber: subscriber, + done: make(chan Done, 1), + } + + // add the state publisher to the engine + engine.statesStreams.Set(publisher.ID(), stateSubscriber) + + // start the state publisher + engine.logger.Infof("starting %s durable state publisher....", publisher.ID()) + go engine.sendState(stateSubscriber) + } + + return nil +} + // parseCommandReply parses the command reply func parseCommandReply(reply *egopb.CommandReply) (State, uint64, error) { var ( @@ -509,3 +660,85 @@ func parseCommandReply(reply *egopb.CommandReply) (State, uint64, error) { } return state, 0, errors.New("no state received") } + +// sendEvent sends events to the event publisher +func (engine *Engine) sendEvent(stream *eventsStream) { + for { + select { + case <-stream.done: + return + case message := <-stream.subscriber.Iterator(): + if message == nil { + continue + } + + event, ok := message.Payload().(*egopb.Event) + if !ok { + continue + } + + publisher := stream.publisher + if err := publisher.Publish(context.Background(), event); err != nil { + engine.logger.Errorf("(%s) failed to publish event=[persistenceID=%s, sequenceNumber=%d]: %s", + publisher.ID(), + event.GetPersistenceId(), + event.GetSequenceNumber(), + err.Error()) + continue + } + + engine.logger.Infof("(%s) successfully published event=[persistenceID=%s, sequenceNumber=%d]: %s", + publisher.ID(), + event.GetPersistenceId(), + event.GetSequenceNumber()) + } + } +} + +// sendState sends state changes to the state publisher +func (engine *Engine) sendState(stream *statesStream) { + for { + select { + case <-stream.done: + return + case message := <-stream.subscriber.Iterator(): + if message == nil { + continue + } + + msg, ok := message.Payload().(*egopb.DurableState) + if !ok { + continue + } + + publisher := stream.publisher + if err := publisher.Publish(context.Background(), msg); err != nil { + engine.logger.Errorf("(%s) failed to publish state=[persistenceID=%s, version=%d]: %s", + publisher.ID(), + msg.GetPersistenceId(), + msg.GetVersionNumber(), + err.Error()) + continue + } + + engine.logger.Infof("(%s) successfully published state=[persistenceID=%s, version=%d]: %s", + publisher.ID(), + msg.GetPersistenceId(), + msg.GetVersionNumber()) + } + } +} + +// generateTopics generates a list of topics based on the base topic name and partitions count. +func generateTopics(baseTopic string, partitionsCount uint64) []string { + var topics []string + switch { + case partitionsCount == 0: + topics = append(topics, fmt.Sprintf(baseTopic, 0)) + default: + for i := 0; i < int(partitionsCount); i++ { + topics = append(topics, fmt.Sprintf(baseTopic, i)) + } + } + return topics +} diff --git a/engine_test.go b/engine_test.go index 8af1c57..5d1cf7e 100644 --- a/engine_test.go +++ b/engine_test.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,6 +26,7 @@ package ego import ( "context" + "crypto/tls" "errors" "net" "strconv" @@ -33,23 +34,27 @@ import ( "time" "github.com/google/uuid" + "github.com/kapetan-io/tackle/autotls" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/tochemey/goakt/v2/actors" - "github.com/tochemey/goakt/v2/log" - mockdisco "github.com/tochemey/goakt/v2/mocks/discovery" + actors "github.com/tochemey/goakt/v3/actor" + "github.com/tochemey/goakt/v3/log" + mockdisco "github.com/tochemey/goakt/v3/mocks/discovery" "github.com/travisjeffery/go-dynaport" "google.golang.org/protobuf/proto" "github.com/tochemey/ego/v3/egopb" samplepb "github.com/tochemey/ego/v3/example/pbs/sample/pb/v1" "github.com/tochemey/ego/v3/internal/lib" + egomock "github.com/tochemey/ego/v3/mocks/ego" "github.com/tochemey/ego/v3/projection" testpb "github.com/tochemey/ego/v3/test/data/pb/v3" testkit2 "github.com/tochemey/ego/v3/testkit" ) -func TestEgo(t *testing.T) { +// nolint +func TestEngine(t *testing.T) { t.Run("EventSourced entity With single node cluster enabled", func(t *testing.T) { ctx := context.TODO() // create the event store @@ -73,7 +78,7 @@ func TestEgo(t *testing.T) { // mock the discovery provider provider := new(mockdisco.Provider) - provider.EXPECT().ID().Return("testDisco") + provider.EXPECT().ID().Return("id") provider.EXPECT().Initialize().Return(nil) provider.EXPECT().Register().Return(nil) provider.EXPECT().Deregister().Return(nil) @@ -83,8 +88,20 @@ func TestEgo(t *testing.T) { // create a projection message handler handler := projection.NewDiscardHandler(log.DiscardLogger) // create the ego engine + // AutoGenerate TLS certs + conf := autotls.Config{ + AutoTLS: true, + ClientAuth: tls.NoClientCert, + InsecureSkipVerify: false, + } + require.NoError(t, autotls.Setup(&conf)) + engine := NewEngine("Sample", eventStore, WithLogger(log.DiscardLogger), + WithTLS(&TLS{ + ClientTLS: conf.ClientTLS, + ServerTLS: conf.ServerTLS, + }), WithCluster(provider, 4, 1, host, remotingPort, gossipPort, clusterPort)) // start ego engine err := engine.Start(ctx) @@ -168,6 +185,7 @@ func TestEgo(t *testing.T) { assert.NoError(t, eventStore.Disconnect(ctx)) assert.NoError(t, offsetStore.Disconnect(ctx)) assert.NoError(t, engine.Stop(ctx)) + provider.AssertExpectations(t) }) t.Run("EventSourced entity With no cluster enabled", func(t *testing.T) { ctx := context.TODO() @@ -175,11 +193,29 @@ func TestEgo(t *testing.T) { eventStore := testkit2.NewEventsStore() // connect to the event store require.NoError(t, eventStore.Connect(ctx)) + + // mock the event publisher + publisher := new(egomock.EventPublisher) + publisher.On("ID").Return("eGo.test.EventsPublisher") + publisher.On("Close", ctx).Return(nil) + publisher. + On("Publish", mock.Anything, mock.AnythingOfType("*egopb.Event")). + Return(func(ctx context.Context, event *egopb.Event) error { + return nil + }) + // create the ego engine engine := NewEngine("Sample", eventStore, WithLogger(log.DiscardLogger)) // start ego engine err := engine.Start(ctx) require.NoError(t, err) + + lib.Pause(time.Second) + + // add the events publisher before start processing events + err = engine.AddEventPublishers(publisher) + require.NoError(t, err) + // create a persistence id entityID := uuid.NewString() // create an entity behavior with a given id @@ -200,9 +236,9 @@ func TestEgo(t *testing.T) { account, ok := resultingState.(*samplepb.Account) require.True(t, ok) - assert.EqualValues(t, 500.00, account.GetAccountBalance()) - assert.Equal(t, entityID, account.GetAccountId()) - assert.EqualValues(t, 1, revision) + require.EqualValues(t, 500.00, account.GetAccountBalance()) + require.Equal(t, entityID, account.GetAccountId()) + require.EqualValues(t, 1, revision) // send another command to credit the balance command = &samplepb.CreditAccount{ @@ -214,13 +250,15 @@ func TestEgo(t *testing.T) { newAccount, ok := newState.(*samplepb.Account) require.True(t, ok) - assert.EqualValues(t, 750.00, newAccount.GetAccountBalance()) - assert.Equal(t, entityID, newAccount.GetAccountId()) - assert.EqualValues(t, 2, revision) + require.EqualValues(t, 750.00, newAccount.GetAccountBalance()) + require.Equal(t, entityID, newAccount.GetAccountId()) + require.EqualValues(t, 2, revision) // free resources - assert.NoError(t, eventStore.Disconnect(ctx)) - assert.NoError(t, engine.Stop(ctx)) + require.NoError(t, eventStore.Disconnect(ctx)) + require.NoError(t, engine.Stop(ctx)) + lib.Pause(time.Second) + publisher.AssertExpectations(t) }) t.Run("EventSourced entity With SendCommand when not started", func(t *testing.T) { ctx := context.TODO() @@ -355,7 +393,6 @@ func TestEgo(t *testing.T) { assert.NoError(t, eventStore.Disconnect(ctx)) }) - t.Run("DurableStore entity With single node cluster enabled", func(t *testing.T) { ctx := context.TODO() stateStore := testkit2.NewDurableStore() @@ -376,7 +413,7 @@ func TestEgo(t *testing.T) { // mock the discovery provider provider := new(mockdisco.Provider) - provider.EXPECT().ID().Return("testDisco") + provider.EXPECT().ID().Return("id") provider.EXPECT().Initialize().Return(nil) provider.EXPECT().Register().Return(nil) provider.EXPECT().Deregister().Return(nil) @@ -394,12 +431,11 @@ func TestEgo(t *testing.T) { // wait for the cluster to fully start lib.Pause(time.Second) - // subscribe to events + // subscribe to durable state subscriber, err := engine.Subscribe() require.NoError(t, err) require.NotNil(t, subscriber) - require.NoError(t, err) // create a persistence id entityID := uuid.NewString() @@ -451,6 +487,7 @@ func TestEgo(t *testing.T) { require.NoError(t, engine.Stop(ctx)) lib.Pause(time.Second) require.NoError(t, stateStore.Disconnect(ctx)) + provider.AssertExpectations(t) }) t.Run("DurableStore entity With no cluster enabled", func(t *testing.T) { ctx := context.TODO() @@ -465,6 +502,13 @@ func TestEgo(t *testing.T) { err := engine.Start(ctx) require.NoError(t, err) + lib.Pause(time.Second) + + // subscribe to durable state + subscriber, err := engine.Subscribe() + require.NoError(t, err) + require.NotNil(t, subscriber) + entityID := uuid.NewString() behavior := NewAccountDurableStateBehavior(entityID) @@ -499,6 +543,13 @@ func TestEgo(t *testing.T) { assert.Equal(t, entityID, newAccount.GetAccountId()) assert.EqualValues(t, 2, revision) + for message := range subscriber.Iterator() { + payload := message.Payload() + envelope, ok := payload.(*egopb.DurableState) + require.True(t, ok) + require.NotZero(t, envelope.GetVersionNumber()) + } + assert.NoError(t, engine.Stop(ctx)) lib.Pause(time.Second) assert.NoError(t, stateStore.Disconnect(ctx)) @@ -565,6 +616,429 @@ func TestEgo(t *testing.T) { assert.NoError(t, engine.Stop(ctx)) assert.NoError(t, stateStore.Disconnect(ctx)) }) + t.Run("With Events Publisher with cluster enabled", func(t *testing.T) { + ctx := context.TODO() + // create the event store + eventStore := testkit2.NewEventsStore() + require.NoError(t, eventStore.Connect(ctx)) + offsetStore := testkit2.NewOffsetStore() + require.NoError(t, offsetStore.Connect(ctx)) + + nodePorts := dynaport.Get(3) + discoveryPort := nodePorts[0] + clusterPort := nodePorts[1] + remotingPort := nodePorts[2] + + host := "127.0.0.1" + + // define discovered addresses + addrs := []string{ + net.JoinHostPort(host, strconv.Itoa(discoveryPort)), + } + + // mock the discovery provider + provider := new(mockdisco.Provider) + + provider.EXPECT().ID().Return("id") + provider.EXPECT().Initialize().Return(nil) + provider.EXPECT().Register().Return(nil) + provider.EXPECT().Deregister().Return(nil) + provider.EXPECT().DiscoverPeers().Return(addrs, nil) + provider.EXPECT().Close().Return(nil) + + // mock the event publisher + publisher := new(egomock.EventPublisher) + publisher.On("ID").Return("eGo.test.EventsPublisher") + publisher.On("Close", ctx).Return(nil) + publisher. + On("Publish", mock.Anything, mock.AnythingOfType("*egopb.Event")). + Return(func(ctx context.Context, event *egopb.Event) error { + return nil + }) + + // create a projection message handler + handler := projection.NewDiscardHandler(log.DiscardLogger) + // create the ego engine + // AutoGenerate TLS certs + conf := autotls.Config{ + AutoTLS: true, + ClientAuth: tls.NoClientCert, + InsecureSkipVerify: false, + } + require.NoError(t, autotls.Setup(&conf)) + + engine := NewEngine("Sample", eventStore, + WithLogger(log.DiscardLogger), + WithTLS(&TLS{ + ClientTLS: conf.ClientTLS, + ServerTLS: conf.ServerTLS, + }), + WithCluster(provider, 4, 1, host, remotingPort, discoveryPort, clusterPort)) + + // start ego engine + err := engine.Start(ctx) + + // wait for the cluster to fully start + lib.Pause(time.Second) + + // add the events publisher before start processing events + err = engine.AddEventPublishers(publisher) + require.NoError(t, err) + + // add projection + err = engine.AddProjection(ctx, "discard", handler, offsetStore) + require.NoError(t, err) + + lib.Pause(time.Second) + + running, err := engine.IsProjectionRunning(ctx, "discard") + require.NoError(t, err) + require.True(t, running) + + // subscribe to events + subscriber, err := engine.Subscribe() + require.NoError(t, err) + require.NotNil(t, subscriber) + + require.NoError(t, err) + // create a persistence id + entityID := uuid.NewString() + // create an entity behavior with a given id + behavior := NewEventSourcedEntity(entityID) + // create an entity + err = engine.Entity(ctx, behavior) + require.NoError(t, err) + // send some commands to the pid + var command proto.Message + // create an account + command = &samplepb.CreateAccount{ + AccountId: entityID, + AccountBalance: 500.00, + } + + // wait for the cluster to fully start + lib.Pause(time.Second) + + // send the command to the actor. Please don't ignore the error in production grid code + resultingState, revision, err := engine.SendCommand(ctx, entityID, command, time.Minute) + require.NoError(t, err) + account, ok := resultingState.(*samplepb.Account) + require.True(t, ok) + + assert.EqualValues(t, 500.00, account.GetAccountBalance()) + assert.Equal(t, entityID, account.GetAccountId()) + assert.EqualValues(t, 1, revision) + + // send another command to credit the balance + command = &samplepb.CreditAccount{ + AccountId: entityID, + Balance: 250, + } + + newState, revision, err := engine.SendCommand(ctx, entityID, command, time.Minute) + require.NoError(t, err) + newAccount, ok := newState.(*samplepb.Account) + require.True(t, ok) + + assert.EqualValues(t, 750.00, newAccount.GetAccountBalance()) + assert.Equal(t, entityID, newAccount.GetAccountId()) + assert.EqualValues(t, 2, revision) + + for message := range subscriber.Iterator() { + payload := message.Payload() + envelope, ok := payload.(*egopb.Event) + event := envelope.GetEvent() + require.True(t, ok) + switch envelope.GetSequenceNumber() { + case 1: + assert.True(t, event.MessageIs(new(samplepb.AccountCreated))) + case 2: + assert.True(t, event.MessageIs(new(samplepb.AccountCredited))) + } + } + + // free resources + assert.NoError(t, eventStore.Disconnect(ctx)) + assert.NoError(t, offsetStore.Disconnect(ctx)) + assert.NoError(t, engine.Stop(ctx)) + lib.Pause(time.Second) + + publisher.AssertExpectations(t) + provider.AssertExpectations(t) + }) + t.Run("With DurableState Publisher with no cluster enabled", func(t *testing.T) { + ctx := context.TODO() + stateStore := testkit2.NewDurableStore() + require.NoError(t, stateStore.Connect(ctx)) + + // mock the state publisher + publisher := new(egomock.StatePublisher) + publisher.On("ID").Return("eGo.test.StatePublisher") + publisher.On("Close", ctx).Return(nil) + publisher. + On("Publish", mock.Anything, mock.AnythingOfType("*egopb.DurableState")). + Return(func(ctx context.Context, state *egopb.DurableState) error { + return nil + }) + + // create the ego engine + engine := NewEngine("Sample", nil, + WithStateStore(stateStore), + WithLogger(log.DiscardLogger)) + + err := engine.Start(ctx) + require.NoError(t, err) + + // wait for complete start + lib.Pause(time.Second) + + // add the state publisher before start processing durable state + err = engine.AddStatePublishers(publisher) + require.NoError(t, err) + + entityID := uuid.NewString() + behavior := NewAccountDurableStateBehavior(entityID) + + err = engine.DurableStateEntity(ctx, behavior) + require.NoError(t, err) + var command proto.Message + + command = &testpb.CreateAccount{ + AccountBalance: 500.00, + } + + resultingState, revision, err := engine.SendCommand(ctx, entityID, command, time.Minute) + require.NoError(t, err) + account, ok := resultingState.(*testpb.Account) + require.True(t, ok) + + require.EqualValues(t, 500.00, account.GetAccountBalance()) + require.Equal(t, entityID, account.GetAccountId()) + require.EqualValues(t, 1, revision) + + // send another command to credit the balance + command = &testpb.CreditAccount{ + AccountId: entityID, + Balance: 250, + } + newState, revision, err := engine.SendCommand(ctx, entityID, command, time.Minute) + require.NoError(t, err) + newAccount, ok := newState.(*testpb.Account) + require.True(t, ok) + + require.EqualValues(t, 750.00, newAccount.GetAccountBalance()) + require.Equal(t, entityID, newAccount.GetAccountId()) + require.EqualValues(t, 2, revision) + + require.NoError(t, engine.Stop(ctx)) + assert.NoError(t, stateStore.Disconnect(ctx)) + lib.Pause(time.Second) + publisher.AssertExpectations(t) + }) + t.Run("With DurableState Publisher with cluster enabled", func(t *testing.T) { + ctx := context.TODO() + stateStore := testkit2.NewDurableStore() + require.NoError(t, stateStore.Connect(ctx)) + + nodePorts := dynaport.Get(3) + gossipPort := nodePorts[0] + clusterPort := nodePorts[1] + remotingPort := nodePorts[2] + + host := "127.0.0.1" + + // define discovered addresses + addrs := []string{ + net.JoinHostPort(host, strconv.Itoa(gossipPort)), + } + + // mock the state publisher + publisher := new(egomock.StatePublisher) + publisher.On("ID").Return("eGo.test.StatePublisher") + publisher.On("Close", ctx).Return(nil) + publisher. + On("Publish", mock.Anything, mock.AnythingOfType("*egopb.DurableState")). + Return(func(ctx context.Context, state *egopb.DurableState) error { + return nil + }) + + // mock the discovery provider + provider := new(mockdisco.Provider) + + provider.EXPECT().ID().Return("id") + provider.EXPECT().Initialize().Return(nil) + provider.EXPECT().Register().Return(nil) + provider.EXPECT().Deregister().Return(nil) + provider.EXPECT().DiscoverPeers().Return(addrs, nil) + provider.EXPECT().Close().Return(nil) + + // create the ego engine + engine := NewEngine("Sample", nil, + WithLogger(log.DiscardLogger), + WithStateStore(stateStore), + WithCluster(provider, 4, 1, host, remotingPort, gossipPort, clusterPort)) + + err := engine.Start(ctx) + + // wait for the cluster to fully start + lib.Pause(time.Second) + + // add the state publisher before start processing durable state + err = engine.AddStatePublishers(publisher) + require.NoError(t, err) + + // subscribe to durable state + subscriber, err := engine.Subscribe() + require.NoError(t, err) + require.NotNil(t, subscriber) + + // create a persistence id + entityID := uuid.NewString() + + behavior := NewAccountDurableStateBehavior(entityID) + // create an entity + err = engine.DurableStateEntity(ctx, behavior) + require.NoError(t, err) + // send some commands to the pid + var command proto.Message + command = &testpb.CreateAccount{ + AccountBalance: 500.00, + } + + // wait for the cluster to fully start + lib.Pause(time.Second) + + // send the command to the actor. Please don't ignore the error in production grid code + resultingState, revision, err := engine.SendCommand(ctx, entityID, command, time.Minute) + require.NoError(t, err) + account, ok := resultingState.(*testpb.Account) + require.True(t, ok) + + assert.EqualValues(t, 500.00, account.GetAccountBalance()) + assert.Equal(t, entityID, account.GetAccountId()) + assert.EqualValues(t, 1, revision) + + command = &testpb.CreditAccount{ + AccountId: entityID, + Balance: 250, + } + + newState, revision, err := engine.SendCommand(ctx, entityID, command, time.Minute) + require.NoError(t, err) + newAccount, ok := newState.(*testpb.Account) + require.True(t, ok) + + assert.EqualValues(t, 750.00, newAccount.GetAccountBalance()) + assert.Equal(t, entityID, newAccount.GetAccountId()) + assert.EqualValues(t, 2, revision) + + for message := range subscriber.Iterator() { + payload := message.Payload() + envelope, ok := payload.(*egopb.DurableState) + require.True(t, ok) + require.NotZero(t, envelope.GetVersionNumber()) + } + + // free resources + require.NoError(t, engine.Stop(ctx)) + lib.Pause(time.Second) + require.NoError(t, stateStore.Disconnect(ctx)) + publisher.AssertExpectations(t) + provider.AssertExpectations(t) + }) + t.Run("With DurableState Publisher when not started", func(t *testing.T) { + ctx := context.TODO() + // create the event store + eventStore := testkit2.NewEventsStore() + require.NoError(t, eventStore.Connect(ctx)) + + publisher := new(egomock.StatePublisher) + + // create the ego engine + engine := NewEngine("Sample", eventStore, WithLogger(log.DiscardLogger)) + err := engine.AddStatePublishers(publisher) + require.Error(t, err) + assert.EqualError(t, err, ErrEngineNotStarted.Error()) + + assert.NoError(t, eventStore.Disconnect(ctx)) + }) + t.Run("With EventPublisher when not started", func(t *testing.T) { + ctx := context.TODO() + // create the event store + eventStore := testkit2.NewEventsStore() + require.NoError(t, eventStore.Connect(ctx)) + + publisher := new(egomock.EventPublisher) + + // create the ego engine + engine := NewEngine("Sample", eventStore, WithLogger(log.DiscardLogger)) + err := engine.AddEventPublishers(publisher) + require.Error(t, err) + assert.EqualError(t, err, ErrEngineNotStarted.Error()) + + assert.NoError(t, eventStore.Disconnect(ctx)) + }) + t.Run("With Engine Stop failure when EventPublisher close fails", func(t *testing.T) { + ctx := context.TODO() + stateStore := testkit2.NewDurableStore() + require.NoError(t, stateStore.Connect(ctx)) + + // mock the state publisher + closeErr := errors.New("close error") + publisher := new(egomock.EventPublisher) + publisher.On("ID").Return("eGo.test.EventPublisher") + publisher.On("Close", ctx).Return(closeErr) + + // create the ego engine + engine := NewEngine("Sample", nil, + WithStateStore(stateStore), + WithLogger(log.DiscardLogger)) + + err := engine.Start(ctx) + require.NoError(t, err) + + // wait for complete start + lib.Pause(time.Second) + + // add the state publisher before start processing durable state + err = engine.AddEventPublishers(publisher) + require.NoError(t, err) + require.ErrorIs(t, engine.Stop(ctx), closeErr) + assert.NoError(t, stateStore.Disconnect(ctx)) + lib.Pause(time.Second) + publisher.AssertExpectations(t) + }) + + t.Run("With Engine Stop failure when DurableState Publisher close fails", func(t *testing.T) { + ctx := context.TODO() + stateStore := testkit2.NewDurableStore() + require.NoError(t, stateStore.Connect(ctx)) + + // mock the state publisher + closeErr := errors.New("close error") + publisher := new(egomock.StatePublisher) + publisher.On("ID").Return("eGo.test.StatePublisher") + publisher.On("Close", ctx).Return(closeErr) + + // create the ego engine + engine := NewEngine("Sample", nil, + WithStateStore(stateStore), + WithLogger(log.DiscardLogger)) + + err := engine.Start(ctx) + require.NoError(t, err) + + // wait for complete start + lib.Pause(time.Second) + + // add the state publisher before start processing durable state + err = engine.AddStatePublishers(publisher) + require.NoError(t, err) + require.ErrorIs(t, engine.Stop(ctx), closeErr) + assert.NoError(t, stateStore.Disconnect(ctx)) + lib.Pause(time.Second) + publisher.AssertExpectations(t) + }) } // EventSourcedEntity implements persistence.Behavior diff --git a/event_sourced_actor.go b/event_sourced_actor.go index d4bf7df..509c572 100644 --- a/event_sourced_actor.go +++ b/event_sourced_actor.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,8 +30,8 @@ import ( "fmt" "time" - "github.com/tochemey/goakt/v2/actors" - "github.com/tochemey/goakt/v2/goaktpb" + goakt "github.com/tochemey/goakt/v3/actor" + "github.com/tochemey/goakt/v3/goaktpb" "golang.org/x/sync/errgroup" "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/timestamppb" @@ -55,8 +55,8 @@ type eventSourcedActor struct { eventsStream eventstream.Stream } -// implements the actors.Actor interface -var _ actors.Actor = (*eventSourcedActor)(nil) +// implements the goakt.Actor interface +var _ goakt.Actor = (*eventSourcedActor)(nil) // newEventSourcedActor creates an instance of actor provided the eventSourcedHandler and the events store func newEventSourcedActor(behavior EventSourcedBehavior, eventsStore persistence.EventsStore, eventsStream eventstream.Stream) *eventSourcedActor { @@ -81,7 +81,7 @@ func (entity *eventSourcedActor) PreStart(ctx context.Context) error { } // Receive processes any message dropped into the actor mailbox. -func (entity *eventSourcedActor) Receive(ctx *actors.ReceiveContext) { +func (entity *eventSourcedActor) Receive(ctx *goakt.ReceiveContext) { switch command := ctx.Message().(type) { case *goaktpb.PostStart: // pass @@ -123,7 +123,7 @@ func (entity *eventSourcedActor) recoverFromSnapshot(ctx context.Context) error } // sendErrorReply sends an error as a reply message -func (entity *eventSourcedActor) sendErrorReply(ctx *actors.ReceiveContext, err error) { +func (entity *eventSourcedActor) sendErrorReply(ctx *goakt.ReceiveContext, err error) { reply := &egopb.CommandReply{ Reply: &egopb.CommandReply_ErrorReply{ ErrorReply: &egopb.ErrorReply{ @@ -136,7 +136,7 @@ func (entity *eventSourcedActor) sendErrorReply(ctx *actors.ReceiveContext, err } // getStateAndReply returns the current state of the entity -func (entity *eventSourcedActor) getStateAndReply(ctx *actors.ReceiveContext) { +func (entity *eventSourcedActor) getStateAndReply(ctx *goakt.ReceiveContext) { latestEvent, err := entity.eventsStore.GetLatestEvent(ctx.Context(), entity.ID()) if err != nil { entity.sendErrorReply(ctx, err) @@ -159,7 +159,7 @@ func (entity *eventSourcedActor) getStateAndReply(ctx *actors.ReceiveContext) { } // processCommandAndReply processes the incoming command -func (entity *eventSourcedActor) processCommandAndReply(ctx *actors.ReceiveContext, command Command) { +func (entity *eventSourcedActor) processCommandAndReply(ctx *goakt.ReceiveContext, command Command) { goCtx := ctx.Context() events, err := entity.HandleCommand(goCtx, command, entity.currentState) if err != nil { @@ -184,7 +184,7 @@ func (entity *eventSourcedActor) processCommandAndReply(ctx *actors.ReceiveConte return } - shardNumber := ctx.Self().ActorSystem().GetPartition(entity.ID()) + shardNumber := ctx.ActorSystem().GetPartition(entity.ID()) topic := fmt.Sprintf(eventsTopic, shardNumber) var envelopes []*egopb.Event @@ -215,6 +215,7 @@ func (entity *eventSourcedActor) processCommandAndReply(ctx *actors.ReceiveConte envelopes = append(envelopes, envelope) } + ctx.Logger().Debugf("publishing events to topic: %s", topic) eg, goCtx := errgroup.WithContext(goCtx) eg.Go(func() error { for _, envelope := range envelopes { diff --git a/event_sourced_actor_test.go b/event_sourced_actor_test.go index b507124..c6e8778 100644 --- a/event_sourced_actor_test.go +++ b/event_sourced_actor_test.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -32,8 +32,8 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tochemey/goakt/v2/actors" - "github.com/tochemey/goakt/v2/log" + goakt "github.com/tochemey/goakt/v3/actor" + "github.com/tochemey/goakt/v3/log" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/emptypb" @@ -48,10 +48,10 @@ func TestEventSourcedActor(t *testing.T) { t.Run("with state reply", func(t *testing.T) { ctx := context.TODO() // create an actor system - actorSystem, err := actors.NewActorSystem("TestActorSystem", - actors.WithPassivationDisabled(), - actors.WithLogger(log.DiscardLogger), - actors.WithActorInitMaxRetries(3)) + actorSystem, err := goakt.NewActorSystem("TestActorSystem", + goakt.WithPassivationDisabled(), + goakt.WithLogger(log.DiscardLogger), + goakt.WithActorInitMaxRetries(3)) require.NoError(t, err) assert.NotNil(t, actorSystem) @@ -89,7 +89,7 @@ func TestEventSourcedActor(t *testing.T) { command = &testpb.CreateAccount{AccountBalance: 500.00} // send the command to the actor - reply, err := actors.Ask(ctx, pid, command, 5*time.Second) + reply, err := goakt.Ask(ctx, pid, command, 5*time.Second) require.NoError(t, err) require.NotNil(t, reply) require.IsType(t, new(egopb.CommandReply), reply) @@ -116,7 +116,7 @@ func TestEventSourcedActor(t *testing.T) { AccountId: persistenceID, Balance: 250, } - reply, err = actors.Ask(ctx, pid, command, 5*time.Second) + reply, err = goakt.Ask(ctx, pid, command, 5*time.Second) require.NoError(t, err) require.NotNil(t, reply) require.IsType(t, new(egopb.CommandReply), reply) @@ -154,10 +154,10 @@ func TestEventSourcedActor(t *testing.T) { ctx := context.TODO() // create an actor system - actorSystem, err := actors.NewActorSystem("TestActorSystem", - actors.WithPassivationDisabled(), - actors.WithLogger(log.DiscardLogger), - actors.WithActorInitMaxRetries(3)) + actorSystem, err := goakt.NewActorSystem("TestActorSystem", + goakt.WithPassivationDisabled(), + goakt.WithLogger(log.DiscardLogger), + goakt.WithActorInitMaxRetries(3)) require.NoError(t, err) assert.NotNil(t, actorSystem) @@ -195,7 +195,7 @@ func TestEventSourcedActor(t *testing.T) { command = &testpb.CreateAccount{AccountBalance: 500.00} // send the command to the actor - reply, err := actors.Ask(ctx, pid, command, time.Second) + reply, err := goakt.Ask(ctx, pid, command, time.Second) require.NoError(t, err) require.NotNil(t, reply) require.IsType(t, new(egopb.CommandReply), reply) @@ -222,7 +222,7 @@ func TestEventSourcedActor(t *testing.T) { AccountId: "different-id", Balance: 250, } - reply, err = actors.Ask(ctx, pid, command, time.Second) + reply, err = goakt.Ask(ctx, pid, command, time.Second) require.NoError(t, err) require.NotNil(t, reply) require.IsType(t, new(egopb.CommandReply), reply) @@ -248,10 +248,10 @@ func TestEventSourcedActor(t *testing.T) { ctx := context.TODO() // create an actor system - actorSystem, err := actors.NewActorSystem("TestActorSystem", - actors.WithPassivationDisabled(), - actors.WithLogger(log.DiscardLogger), - actors.WithActorInitMaxRetries(3)) + actorSystem, err := goakt.NewActorSystem("TestActorSystem", + goakt.WithPassivationDisabled(), + goakt.WithLogger(log.DiscardLogger), + goakt.WithActorInitMaxRetries(3)) require.NoError(t, err) assert.NotNil(t, actorSystem) @@ -285,7 +285,7 @@ func TestEventSourcedActor(t *testing.T) { command := &testpb.TestSend{} // send the command to the actor - reply, err := actors.Ask(ctx, pid, command, time.Second) + reply, err := goakt.Ask(ctx, pid, command, time.Second) require.NoError(t, err) require.NotNil(t, reply) require.IsType(t, new(egopb.CommandReply), reply) @@ -309,10 +309,10 @@ func TestEventSourcedActor(t *testing.T) { ctx := context.TODO() // create an actor system - actorSystem, err := actors.NewActorSystem("TestActorSystem", - actors.WithPassivationDisabled(), - actors.WithLogger(log.DiscardLogger), - actors.WithActorInitMaxRetries(3), + actorSystem, err := goakt.NewActorSystem("TestActorSystem", + goakt.WithPassivationDisabled(), + goakt.WithLogger(log.DiscardLogger), + goakt.WithActorInitMaxRetries(3), ) require.NoError(t, err) assert.NotNil(t, actorSystem) @@ -355,7 +355,7 @@ func TestEventSourcedActor(t *testing.T) { command = &testpb.CreateAccount{AccountBalance: 500.00} // send the command to the actor - reply, err := actors.Ask(ctx, pid, command, time.Second) + reply, err := goakt.Ask(ctx, pid, command, time.Second) require.NoError(t, err) require.NotNil(t, reply) require.IsType(t, new(egopb.CommandReply), reply) @@ -382,7 +382,7 @@ func TestEventSourcedActor(t *testing.T) { AccountId: persistenceID, Balance: 250, } - reply, err = actors.Ask(ctx, pid, command, time.Second) + reply, err = goakt.Ask(ctx, pid, command, time.Second) require.NoError(t, err) require.NotNil(t, reply) require.IsType(t, new(egopb.CommandReply), reply) @@ -415,7 +415,7 @@ func TestEventSourcedActor(t *testing.T) { // fetch the current state command = &egopb.GetStateCommand{} - reply, err = actors.Ask(ctx, pid, command, time.Second) + reply, err = goakt.Ask(ctx, pid, command, time.Second) require.NoError(t, err) require.NotNil(t, reply) require.IsType(t, new(egopb.CommandReply), reply) @@ -446,10 +446,10 @@ func TestEventSourcedActor(t *testing.T) { ctx := context.TODO() // create an actor system - actorSystem, err := actors.NewActorSystem("TestActorSystem", - actors.WithPassivationDisabled(), - actors.WithLogger(log.DiscardLogger), - actors.WithActorInitMaxRetries(3)) + actorSystem, err := goakt.NewActorSystem("TestActorSystem", + goakt.WithPassivationDisabled(), + goakt.WithLogger(log.DiscardLogger), + goakt.WithActorInitMaxRetries(3)) require.NoError(t, err) assert.NotNil(t, actorSystem) @@ -487,7 +487,7 @@ func TestEventSourcedActor(t *testing.T) { command = &testpb.CreateAccount{AccountBalance: 500.00} // send the command to the actor - reply, err := actors.Ask(ctx, pid, command, 5*time.Second) + reply, err := goakt.Ask(ctx, pid, command, 5*time.Second) require.NoError(t, err) require.NotNil(t, reply) require.IsType(t, new(egopb.CommandReply), reply) @@ -515,7 +515,7 @@ func TestEventSourcedActor(t *testing.T) { AccountId: persistenceID, Balance: 250, } - reply, err = actors.Ask(ctx, pid, command, 5*time.Second) + reply, err = goakt.Ask(ctx, pid, command, 5*time.Second) require.NoError(t, err) require.NotNil(t, reply) require.IsType(t, new(egopb.CommandReply), reply) @@ -540,7 +540,7 @@ func TestEventSourcedActor(t *testing.T) { // test no events to persist command = new(testpb.TestNoEvent) // send a command - reply, err = actors.Ask(ctx, pid, command, 5*time.Second) + reply, err = goakt.Ask(ctx, pid, command, 5*time.Second) require.NoError(t, err) require.NotNil(t, reply) commandReply = reply.(*egopb.CommandReply) @@ -573,10 +573,10 @@ func TestEventSourcedActor(t *testing.T) { t.Run("with unhandled event", func(t *testing.T) { ctx := context.TODO() // create an actor system - actorSystem, err := actors.NewActorSystem("TestActorSystem", - actors.WithPassivationDisabled(), - actors.WithLogger(log.DiscardLogger), - actors.WithActorInitMaxRetries(3)) + actorSystem, err := goakt.NewActorSystem("TestActorSystem", + goakt.WithPassivationDisabled(), + goakt.WithLogger(log.DiscardLogger), + goakt.WithActorInitMaxRetries(3)) require.NoError(t, err) assert.NotNil(t, actorSystem) @@ -611,7 +611,7 @@ func TestEventSourcedActor(t *testing.T) { lib.Pause(time.Second) // send the command to the actor - reply, err := actors.Ask(ctx, pid, &testpb.CreateAccount{AccountBalance: 500.00}, 5*time.Second) + reply, err := goakt.Ask(ctx, pid, &testpb.CreateAccount{AccountBalance: 500.00}, 5*time.Second) require.NoError(t, err) require.NotNil(t, reply) require.IsType(t, new(egopb.CommandReply), reply) @@ -633,7 +633,7 @@ func TestEventSourcedActor(t *testing.T) { } assert.True(t, proto.Equal(expected, resultingState)) - reply, err = actors.Ask(ctx, pid, new(emptypb.Empty), 5*time.Second) + reply, err = goakt.Ask(ctx, pid, new(emptypb.Empty), 5*time.Second) require.NoError(t, err) require.NotNil(t, reply) require.IsType(t, new(egopb.CommandReply), reply) diff --git a/eventstream/message.go b/eventstream/message.go index 392df4d..49b5384 100644 --- a/eventstream/message.go +++ b/eventstream/message.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/eventstream/stream.go b/eventstream/stream.go index 8730e14..49415c9 100644 --- a/eventstream/stream.go +++ b/eventstream/stream.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/eventstream/stream_test.go b/eventstream/stream_test.go index 5aa5971..636aed6 100644 --- a/eventstream/stream_test.go +++ b/eventstream/stream_test.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/eventstream/subscriber.go b/eventstream/subscriber.go index 4f7b59f..a57f448 100644 --- a/eventstream/subscriber.go +++ b/eventstream/subscriber.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,20 +28,21 @@ import ( "sync" "github.com/google/uuid" + "go.uber.org/atomic" "github.com/tochemey/ego/v3/internal/queue" ) // Subscriber defines the Subscriber Interface type Subscriber interface { + ID() string + Active() bool Topics() []string Iterator() chan *Message Shutdown() signal(message *Message) subscribe(topic string) unsubscribe(topic string) - Active() bool - ID() string } // subscriber defines the subscriber @@ -55,7 +56,7 @@ type subscriber struct { // topics define the topic the subscriber subscribed to topics map[string]bool // states whether the given subscriber is active or not - active bool + active *atomic.Bool } var _ Subscriber = &subscriber{} @@ -72,7 +73,7 @@ func newSubscriber() *subscriber { sem: sync.Mutex{}, messages: queue.NewQueue(), topics: make(map[string]bool), - active: true, + active: atomic.NewBool(true), } } @@ -87,11 +88,7 @@ func (x *subscriber) ID() string { // Active checks whether the consumer is active func (x *subscriber) Active() bool { - // acquire the lock - x.sem.Lock() - // release the lock once done - defer x.sem.Unlock() - return x.active + return x.active.Load() } // Topics returns the list of topics the consumer has subscribed to @@ -109,23 +106,21 @@ func (x *subscriber) Topics() []string { // Shutdown shutdowns the consumer func (x *subscriber) Shutdown() { - // acquire the lock - x.sem.Lock() - // release the lock once done - defer x.sem.Unlock() - x.active = false + x.active.Store(false) } func (x *subscriber) Iterator() chan *Message { - out := make(chan *Message, x.messages.Length()) - defer close(out) - for { - msg := x.messages.Dequeue() - if msg == nil { - break + out := make(chan *Message) + go func() { + defer close(out) + for x.active.Load() && x.messages.Length() > 0 { + msg := x.messages.Dequeue() + if msg == nil { + break + } + out <- msg.(*Message) } - out <- msg.(*Message) - } + }() return out } @@ -136,7 +131,7 @@ func (x *subscriber) signal(message *Message) { // release the lock once done defer x.sem.Unlock() // only receive message when active - if x.active { + if x.active.Load() { x.messages.Enqueue(message) } } diff --git a/example/durablestate/main.go b/example/durablestate/main.go index 68c5a30..65fa047 100644 --- a/example/durablestate/main.go +++ b/example/durablestate/main.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/example/eventssourced/main.go b/example/eventssourced/main.go index 94e9d86..f10a72e 100644 --- a/example/eventssourced/main.go +++ b/example/eventssourced/main.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/go.mod b/go.mod index 828c9cb..af198a5 100644 --- a/go.mod +++ b/go.mod @@ -1,19 +1,20 @@ module github.com/tochemey/ego/v3 -go 1.22.0 +go 1.23.0 require ( github.com/deckarep/golang-set/v2 v2.7.0 github.com/flowchartsman/retry v1.2.0 github.com/google/uuid v1.6.0 + github.com/kapetan-io/tackle v0.11.0 github.com/stretchr/testify v1.10.0 - github.com/tochemey/goakt/v2 v2.13.0 + github.com/tochemey/goakt/v3 v3.0.0 github.com/travisjeffery/go-dynaport v1.0.0 go.uber.org/atomic v1.11.0 go.uber.org/goleak v1.3.0 go.uber.org/multierr v1.11.0 - golang.org/x/sync v0.10.0 - google.golang.org/protobuf v1.36.4 + golang.org/x/sync v0.11.0 + google.golang.org/protobuf v1.36.5 ) require ( @@ -60,37 +61,37 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/redis/go-redis/v9 v9.7.0 // indirect - github.com/reugn/go-quartz v0.13.0 // indirect + github.com/reugn/go-quartz v0.14.0 // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/tidwall/btree v1.7.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/redcon v1.6.2 // indirect - github.com/tochemey/olric v0.2.0-alpha // indirect + github.com/tochemey/olric v0.2.0 // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/zeebo/xxh3 v1.0.2 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/mod v0.22.0 // indirect - golang.org/x/net v0.34.0 // indirect - golang.org/x/oauth2 v0.25.0 // indirect - golang.org/x/sys v0.29.0 // indirect - golang.org/x/term v0.28.0 // indirect - golang.org/x/text v0.21.0 // indirect - golang.org/x/time v0.9.0 // indirect + golang.org/x/mod v0.23.0 // indirect + golang.org/x/net v0.35.0 // indirect + golang.org/x/oauth2 v0.26.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/term v0.29.0 // indirect + golang.org/x/text v0.22.0 // indirect + golang.org/x/time v0.10.0 // indirect golang.org/x/tools v0.29.0 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.31.4 // indirect - k8s.io/apimachinery v0.31.4 // indirect - k8s.io/client-go v0.31.4 // indirect + k8s.io/api v0.32.2 // indirect + k8s.io/apimachinery v0.32.2 // indirect + k8s.io/client-go v0.32.2 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20241009091222-67ed5848f094 // indirect - k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect + k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect + sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index 07c40d0..2708957 100644 --- a/go.sum +++ b/go.sum @@ -91,8 +91,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k= -github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -131,6 +131,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kapetan-io/tackle v0.11.0 h1:xcQ2WgES8rjsd0ZMBfFTMuCs8YG4+1r2OAPY0+mHXjM= +github.com/kapetan-io/tackle v0.11.0/go.mod h1:94m0H3j8pm9JMsAuqBsC/Y08WpAUh01ugkFxABjjHd8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= @@ -171,16 +173,16 @@ github.com/nats-io/jwt/v2 v2.7.3 h1:6bNPK+FXgBeAqdj4cYQ0F8ViHRbi7woQLq4W29nUAzE= github.com/nats-io/jwt/v2 v2.7.3/go.mod h1:GvkcbHhKquj3pkioy5put1wvPxs78UlZ7D/pY+BgZk4= github.com/nats-io/nats-server/v2 v2.10.25 h1:J0GWLDDXo5HId7ti/lTmBfs+lzhmu8RPkoKl0eSCqwc= github.com/nats-io/nats-server/v2 v2.10.25/go.mod h1:/YYYQO7cuoOBt+A7/8cVjuhWTaTUEAlZbJT+3sMAfFU= -github.com/nats-io/nats.go v1.38.0 h1:A7P+g7Wjp4/NWqDOOP/K6hfhr54DvdDQUznt5JFg9XA= -github.com/nats-io/nats.go v1.38.0/go.mod h1:IGUM++TwokGnXPs82/wCuiHS02/aKrdYUQkU8If6yjw= +github.com/nats-io/nats.go v1.39.0 h1:2/yg2JQjiYYKLwDuBzV0FbB2sIV+eFNkEevlRi4n9lI= +github.com/nats-io/nats.go v1.39.0/go.mod h1:MgRb8oOdigA6cYpEPhXJuRVH6UE/V4jblJ2jQ27IXYM= github.com/nats-io/nkeys v0.4.9 h1:qe9Faq2Gxwi6RZnZMXfmGMZkg3afLLOtrU+gDZJ35b0= github.com/nats-io/nkeys v0.4.9/go.mod h1:jcMqs+FLG+W5YO36OX6wFIFcmpdAns+w1Wm6D3I/evE= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= -github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= -github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= -github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= +github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= +github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= @@ -210,8 +212,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= -github.com/reugn/go-quartz v0.13.0 h1:0eMxvj28Qu1npIDdN9Mzg9hwyksGH6XJt4Cz0QB8EUk= -github.com/reugn/go-quartz v0.13.0/go.mod h1:0ghKksELp8MJ4h84T203aTHRF3Kug5BrxEW3ErBvhzY= +github.com/reugn/go-quartz v0.14.0 h1:KlIBAsOIw1JI8Rc7/f8VrrHBHOr+BiqrTiB35pRe84M= +github.com/reugn/go-quartz v0.14.0/go.mod h1:00DVnBKq2Fxag/HlR9mGXjmHNlMFQ1n/LNM+Fn0jUaE= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= @@ -241,10 +243,10 @@ github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT github.com/tidwall/redcon v1.6.2 h1:5qfvrrybgtO85jnhSravmkZyC0D+7WstbfCs3MmPhow= github.com/tidwall/redcon v1.6.2/go.mod h1:p5Wbsgeyi2VSTBWOcA5vRXrOb9arFTcU2+ZzFjqV75Y= github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= -github.com/tochemey/goakt/v2 v2.13.0 h1:yOiXVuqllHGe4YvvjkBmy/zyGE+o2m4rKw786XEi5XM= -github.com/tochemey/goakt/v2 v2.13.0/go.mod h1:oFq9MUV1w3dCB2gEpy1onsfS/9m8EJ06F2c290o2Nrk= -github.com/tochemey/olric v0.2.0-alpha h1:pj6haxwPhIuLcTGwhsSNEoryPdPXMcEIWBo0EJI8mMA= -github.com/tochemey/olric v0.2.0-alpha/go.mod h1:x0Q6soq7bk0rWSDFPzE4fA8a5m+MnWxHRHZzg/ywg0M= +github.com/tochemey/goakt/v3 v3.0.0 h1:WukWOsXbaCkIjXWKm0GHY81s6yKeHDE06Vj3O42cJ4c= +github.com/tochemey/goakt/v3 v3.0.0/go.mod h1:I3F45cKjyQiWnTqY4PLcnM9hBYhzb3vjulUoD9ComNc= +github.com/tochemey/olric v0.2.0 h1:y/uoVNNVdMn+pbUI01i6TqeD48+VgjRcoRGOzVzkdVY= +github.com/tochemey/olric v0.2.0/go.mod h1:vRpB3Xb0L4J7acugeAfrIXcwLDlCwRsC10s1jCYND1Y= github.com/travisjeffery/go-dynaport v1.0.0 h1:m/qqf5AHgB96CMMSworIPyo1i7NZueRsnwdzdCJ8Ajw= github.com/travisjeffery/go-dynaport v1.0.0/go.mod h1:0LHuDS4QAx+mAc4ri3WkQdavgVoBIZ7cE9ob17KIAJk= github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= @@ -273,12 +275,12 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -288,19 +290,19 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= -golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= +golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -314,17 +316,17 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= -golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= -golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= +golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -344,8 +346,8 @@ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miE google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= -google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -359,28 +361,25 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.31.4 h1:I2QNzitPVsPeLQvexMEsj945QumYraqv9m74isPDKhM= -k8s.io/api v0.31.4/go.mod h1:d+7vgXLvmcdT1BCo79VEgJxHHryww3V5np2OYTr6jdw= -k8s.io/apimachinery v0.31.4 h1:8xjE2C4CzhYVm9DGf60yohpNUh5AEBnPxCryPBECmlM= -k8s.io/apimachinery v0.31.4/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/client-go v0.31.4 h1:t4QEXt4jgHIkKKlx06+W3+1JOwAFU/2OPiOo7H92eRQ= -k8s.io/client-go v0.31.4/go.mod h1:kvuMro4sFYIa8sulL5Gi5GFqUPvfH2O/dXuKstbaaeg= +k8s.io/api v0.32.2 h1:bZrMLEkgizC24G9eViHGOPbW+aRo9duEISRIJKfdJuw= +k8s.io/api v0.32.2/go.mod h1:hKlhk4x1sJyYnHENsrdCWw31FEmCijNGPJO5WzHiJ6Y= +k8s.io/apimachinery v0.32.2 h1:yoQBR9ZGkA6Rgmhbp/yuT9/g+4lxtsGYwW6dR6BDPLQ= +k8s.io/apimachinery v0.32.2/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/client-go v0.32.2 h1:4dYCD4Nz+9RApM2b/3BtVvBHw54QjMFUl1OLcJG5yOA= +k8s.io/client-go v0.32.2/go.mod h1:fpZ4oJXclZ3r2nDOv+Ux3XcJutfrwjKTCHz2H3sww94= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20241009091222-67ed5848f094 h1:MErs8YA0abvOqJ8gIupA1Tz6PKXYUw34XsGlA7uSL1k= -k8s.io/kube-openapi v0.0.0-20241009091222-67ed5848f094/go.mod h1:7ioBJr1A6igWjsR2fxq2EZ0mlMwYLejazSIc2bzMp2U= -k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 h1:MDF6h2H/h4tbzmtIKTuctcwZmY0tY9mD9fNT47QO6HI= -k8s.io/utils v0.0.0-20240921022957-49e7df575cb6/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg= +k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas= +k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= +k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk= +sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/helper_test.go b/helper_test.go index 4806b8b..1ab6b25 100644 --- a/helper_test.go +++ b/helper_test.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/internal/errorschain/errorschain.go b/internal/errorschain/errorschain.go index 9855a91..70a81ab 100644 --- a/internal/errorschain/errorschain.go +++ b/internal/errorschain/errorschain.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/internal/errorschain/errorschain_test.go b/internal/errorschain/errorschain_test.go index 3424071..2271250 100644 --- a/internal/errorschain/errorschain_test.go +++ b/internal/errorschain/errorschain_test.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/internal/lib/lib.go b/internal/lib/lib.go index 27406b1..d166f7b 100644 --- a/internal/lib/lib.go +++ b/internal/lib/lib.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/internal/queue/queue.go b/internal/queue/queue.go index 7c21fbf..b809cac 100644 --- a/internal/queue/queue.go +++ b/internal/queue/queue.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/internal/queue/queue_test.go b/internal/queue/queue_test.go index 10eb5b6..fd41705 100644 --- a/internal/queue/queue_test.go +++ b/internal/queue/queue_test.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/internal/syncmap/map.go b/internal/syncmap/map.go new file mode 100644 index 0000000..ead8b88 --- /dev/null +++ b/internal/syncmap/map.go @@ -0,0 +1,122 @@ +/* + * MIT License + * + * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package syncmap + +import "sync" + +// Map is a generic, concurrency-safe map that allows storing key-value pairs +// while ensuring thread safety using a read-write mutex. +// +// K represents the key type, which must be comparable. +// V represents the value type, which can be any type. +type Map[K comparable, V any] struct { + mu sync.RWMutex + data map[K]V +} + +// New creates and returns a new instance of Map. +// It initializes the internal map for storing key-value pairs. +// +// Example usage: +// +// sm := New[string, int]() +// sm.Set("foo", 42) +// value, ok := sm.Get("foo") +func New[K comparable, V any]() *Map[K, V] { + return &Map[K, V]{ + data: make(map[K]V), + } +} + +// Set stores a key-value pair in the Map. +// If the key already exists, its value is updated. +// +// This method acquires a write lock to ensure safe concurrent access. +func (s *Map[K, V]) Set(k K, v V) { + s.mu.Lock() + s.data[k] = v + s.mu.Unlock() +} + +// Get retrieves the value associated with the given key from the Map. +// The second return value indicates whether the key was found. +// +// This method acquires a read lock to ensure safe concurrent access. +func (s *Map[K, V]) Get(k K) (V, bool) { + s.mu.RLock() + val, ok := s.data[k] + s.mu.RUnlock() + return val, ok +} + +// Delete removes the key-value pair associated with the given key from the Map. +// If the key does not exist, this operation has no effect. +// +// This method acquires a write lock to ensure safe concurrent access. +func (s *Map[K, V]) Delete(k K) { + s.mu.Lock() + delete(s.data, k) + s.mu.Unlock() +} + +// Len returns the number of key-value pairs currently stored in the Map. +// +// This method acquires a read lock to ensure safe concurrent access. +func (s *Map[K, V]) Len() int { + s.mu.RLock() + l := len(s.data) + s.mu.RUnlock() + return l +} + +// Range iterates over all key-value pairs in the Map and executes the given function `f` +// for each pair. The iteration order is not guaranteed. +// +// This method acquires a read lock to ensure safe concurrent access. +func (s *Map[K, V]) Range(f func(K, V)) { + s.mu.RLock() + defer s.mu.RUnlock() + for k, v := range s.data { + f(k, v) + } +} + +// Values returns the values in the Map +func (s *Map[K, V]) Values() []V { + s.mu.RLock() + defer s.mu.RUnlock() + values := make([]V, 0, len(s.data)) + for _, v := range s.data { + values = append(values, v) + } + return values +} + +// Reset clears all key-value pairs from the Map. +func (s *Map[K, V]) Reset() { + s.mu.Lock() + clear(s.data) + s.mu.Unlock() +} diff --git a/internal/syncmap/map_test.go b/internal/syncmap/map_test.go new file mode 100644 index 0000000..a20f44b --- /dev/null +++ b/internal/syncmap/map_test.go @@ -0,0 +1,87 @@ +/* + * MIT License + * + * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package syncmap + +import ( + "slices" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNewAndSet(t *testing.T) { + sm := New[int, string]() + sm.Set(1, "one") + sm.Set(2, "two") + assert.Exactly(t, 2, sm.Len()) +} + +func TestGet(t *testing.T) { + sm := New[int, string]() + sm.Set(1, "one") + + val, ok := sm.Get(1) + require.True(t, ok) + require.Equal(t, "one", val) + + _, ok = sm.Get(2) + require.False(t, ok) +} + +func TestDelete(t *testing.T) { + sm := New[int, string]() + sm.Set(1, "one") + sm.Delete(1) + _, ok := sm.Get(1) + require.False(t, ok) + sm.Delete(2) // just make sure this doesn't panic +} + +func TestLen(t *testing.T) { + sm := New[int, string]() + sm.Set(1, "one") + sm.Set(2, "two") + sm.Set(3, "three") + sm.Delete(2) + assert.Exactly(t, 2, sm.Len()) +} + +func TestForEach(t *testing.T) { + sm := New[int, string]() + sm.Set(1, "one") + sm.Set(2, "two") + + keys := make([]int, 0) + sm.Range(func(k int, v string) { // nolint + keys = append(keys, k) + }) + + assert.Exactly(t, 2, len(keys)) + // Check if keys 1 and 2 are present + if !slices.Contains(keys, 1) || !slices.Contains(keys, 2) { + t.Errorf("Expected keys 1 and 2, got %v", keys) + } +} diff --git a/internal/ticker/ticker.go b/internal/ticker/ticker.go index d53fe31..363e9d5 100644 --- a/internal/ticker/ticker.go +++ b/internal/ticker/ticker.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/internal/ticker/ticker_test.go b/internal/ticker/ticker_test.go index 9ec7c14..76c9415 100644 --- a/internal/ticker/ticker_test.go +++ b/internal/ticker/ticker_test.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/mocks/ego/event_publisher.go b/mocks/ego/event_publisher.go new file mode 100644 index 0000000..5d8b734 --- /dev/null +++ b/mocks/ego/event_publisher.go @@ -0,0 +1,175 @@ +// Code generated by mockery. DO NOT EDIT. + +package ego + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" + egopb "github.com/tochemey/ego/v3/egopb" +) + +// EventPublisher is an autogenerated mock type for the EventPublisher type +type EventPublisher struct { + mock.Mock +} + +type EventPublisher_Expecter struct { + mock *mock.Mock +} + +func (_m *EventPublisher) EXPECT() *EventPublisher_Expecter { + return &EventPublisher_Expecter{mock: &_m.Mock} +} + +// Close provides a mock function with given fields: _a0 +func (_m *EventPublisher) Close(_a0 context.Context) error { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for Close") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// EventPublisher_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' +type EventPublisher_Close_Call struct { + *mock.Call +} + +// Close is a helper method to define mock.On call +// - _a0 context.Context +func (_e *EventPublisher_Expecter) Close(_a0 interface{}) *EventPublisher_Close_Call { + return &EventPublisher_Close_Call{Call: _e.mock.On("Close", _a0)} +} + +func (_c *EventPublisher_Close_Call) Run(run func(_a0 context.Context)) *EventPublisher_Close_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *EventPublisher_Close_Call) Return(_a0 error) *EventPublisher_Close_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *EventPublisher_Close_Call) RunAndReturn(run func(context.Context) error) *EventPublisher_Close_Call { + _c.Call.Return(run) + return _c +} + +// ID provides a mock function with no fields +func (_m *EventPublisher) ID() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for ID") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// EventPublisher_ID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ID' +type EventPublisher_ID_Call struct { + *mock.Call +} + +// ID is a helper method to define mock.On call +func (_e *EventPublisher_Expecter) ID() *EventPublisher_ID_Call { + return &EventPublisher_ID_Call{Call: _e.mock.On("ID")} +} + +func (_c *EventPublisher_ID_Call) Run(run func()) *EventPublisher_ID_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *EventPublisher_ID_Call) Return(_a0 string) *EventPublisher_ID_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *EventPublisher_ID_Call) RunAndReturn(run func() string) *EventPublisher_ID_Call { + _c.Call.Return(run) + return _c +} + +// Publish provides a mock function with given fields: ctx, event +func (_m *EventPublisher) Publish(ctx context.Context, event *egopb.Event) error { + ret := _m.Called(ctx, event) + + if len(ret) == 0 { + panic("no return value specified for Publish") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *egopb.Event) error); ok { + r0 = rf(ctx, event) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// EventPublisher_Publish_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Publish' +type EventPublisher_Publish_Call struct { + *mock.Call +} + +// Publish is a helper method to define mock.On call +// - ctx context.Context +// - event *egopb.Event +func (_e *EventPublisher_Expecter) Publish(ctx interface{}, event interface{}) *EventPublisher_Publish_Call { + return &EventPublisher_Publish_Call{Call: _e.mock.On("Publish", ctx, event)} +} + +func (_c *EventPublisher_Publish_Call) Run(run func(ctx context.Context, event *egopb.Event)) *EventPublisher_Publish_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*egopb.Event)) + }) + return _c +} + +func (_c *EventPublisher_Publish_Call) Return(_a0 error) *EventPublisher_Publish_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *EventPublisher_Publish_Call) RunAndReturn(run func(context.Context, *egopb.Event) error) *EventPublisher_Publish_Call { + _c.Call.Return(run) + return _c +} + +// NewEventPublisher creates a new instance of EventPublisher. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewEventPublisher(t interface { + mock.TestingT + Cleanup(func()) +}) *EventPublisher { + mock := &EventPublisher{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/ego/state_publisher.go b/mocks/ego/state_publisher.go new file mode 100644 index 0000000..4269ab3 --- /dev/null +++ b/mocks/ego/state_publisher.go @@ -0,0 +1,175 @@ +// Code generated by mockery. DO NOT EDIT. + +package ego + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" + egopb "github.com/tochemey/ego/v3/egopb" +) + +// StatePublisher is an autogenerated mock type for the StatePublisher type +type StatePublisher struct { + mock.Mock +} + +type StatePublisher_Expecter struct { + mock *mock.Mock +} + +func (_m *StatePublisher) EXPECT() *StatePublisher_Expecter { + return &StatePublisher_Expecter{mock: &_m.Mock} +} + +// Close provides a mock function with given fields: _a0 +func (_m *StatePublisher) Close(_a0 context.Context) error { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for Close") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// StatePublisher_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' +type StatePublisher_Close_Call struct { + *mock.Call +} + +// Close is a helper method to define mock.On call +// - _a0 context.Context +func (_e *StatePublisher_Expecter) Close(_a0 interface{}) *StatePublisher_Close_Call { + return &StatePublisher_Close_Call{Call: _e.mock.On("Close", _a0)} +} + +func (_c *StatePublisher_Close_Call) Run(run func(_a0 context.Context)) *StatePublisher_Close_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *StatePublisher_Close_Call) Return(_a0 error) *StatePublisher_Close_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *StatePublisher_Close_Call) RunAndReturn(run func(context.Context) error) *StatePublisher_Close_Call { + _c.Call.Return(run) + return _c +} + +// ID provides a mock function with no fields +func (_m *StatePublisher) ID() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for ID") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// StatePublisher_ID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ID' +type StatePublisher_ID_Call struct { + *mock.Call +} + +// ID is a helper method to define mock.On call +func (_e *StatePublisher_Expecter) ID() *StatePublisher_ID_Call { + return &StatePublisher_ID_Call{Call: _e.mock.On("ID")} +} + +func (_c *StatePublisher_ID_Call) Run(run func()) *StatePublisher_ID_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *StatePublisher_ID_Call) Return(_a0 string) *StatePublisher_ID_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *StatePublisher_ID_Call) RunAndReturn(run func() string) *StatePublisher_ID_Call { + _c.Call.Return(run) + return _c +} + +// Publish provides a mock function with given fields: ctx, state +func (_m *StatePublisher) Publish(ctx context.Context, state *egopb.DurableState) error { + ret := _m.Called(ctx, state) + + if len(ret) == 0 { + panic("no return value specified for Publish") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *egopb.DurableState) error); ok { + r0 = rf(ctx, state) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// StatePublisher_Publish_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Publish' +type StatePublisher_Publish_Call struct { + *mock.Call +} + +// Publish is a helper method to define mock.On call +// - ctx context.Context +// - state *egopb.DurableState +func (_e *StatePublisher_Expecter) Publish(ctx interface{}, state interface{}) *StatePublisher_Publish_Call { + return &StatePublisher_Publish_Call{Call: _e.mock.On("Publish", ctx, state)} +} + +func (_c *StatePublisher_Publish_Call) Run(run func(ctx context.Context, state *egopb.DurableState)) *StatePublisher_Publish_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*egopb.DurableState)) + }) + return _c +} + +func (_c *StatePublisher_Publish_Call) Return(_a0 error) *StatePublisher_Publish_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *StatePublisher_Publish_Call) RunAndReturn(run func(context.Context, *egopb.DurableState) error) *StatePublisher_Publish_Call { + _c.Call.Return(run) + return _c +} + +// NewStatePublisher creates a new instance of StatePublisher. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewStatePublisher(t interface { + mock.TestingT + Cleanup(func()) +}) *StatePublisher { + mock := &StatePublisher{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/offsetstore/offset_store.go b/mocks/offsetstore/offset_store.go index 3f5fb3d..87bfff5 100644 --- a/mocks/offsetstore/offset_store.go +++ b/mocks/offsetstore/offset_store.go @@ -26,6 +26,10 @@ func (_m *OffsetStore) EXPECT() *OffsetStore_Expecter { func (_m *OffsetStore) Connect(ctx context.Context) error { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for Connect") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) @@ -68,6 +72,10 @@ func (_c *OffsetStore_Connect_Call) RunAndReturn(run func(context.Context) error func (_m *OffsetStore) Disconnect(ctx context.Context) error { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for Disconnect") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) @@ -110,6 +118,10 @@ func (_c *OffsetStore_Disconnect_Call) RunAndReturn(run func(context.Context) er func (_m *OffsetStore) GetCurrentOffset(ctx context.Context, projectionID *egopb.ProjectionId) (*egopb.Offset, error) { ret := _m.Called(ctx, projectionID) + if len(ret) == 0 { + panic("no return value specified for GetCurrentOffset") + } + var r0 *egopb.Offset var r1 error if rf, ok := ret.Get(0).(func(context.Context, *egopb.ProjectionId) (*egopb.Offset, error)); ok { @@ -165,6 +177,10 @@ func (_c *OffsetStore_GetCurrentOffset_Call) RunAndReturn(run func(context.Conte func (_m *OffsetStore) Ping(ctx context.Context) error { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for Ping") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) @@ -207,6 +223,10 @@ func (_c *OffsetStore_Ping_Call) RunAndReturn(run func(context.Context) error) * func (_m *OffsetStore) ResetOffset(ctx context.Context, projectionName string, value int64) error { ret := _m.Called(ctx, projectionName, value) + if len(ret) == 0 { + panic("no return value specified for ResetOffset") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, int64) error); ok { r0 = rf(ctx, projectionName, value) @@ -251,6 +271,10 @@ func (_c *OffsetStore_ResetOffset_Call) RunAndReturn(run func(context.Context, s func (_m *OffsetStore) WriteOffset(ctx context.Context, offset *egopb.Offset) error { ret := _m.Called(ctx, offset) + if len(ret) == 0 { + panic("no return value specified for WriteOffset") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *egopb.Offset) error); ok { r0 = rf(ctx, offset) diff --git a/mocks/persistence/events_store.go b/mocks/persistence/events_store.go index 2c5d6a1..95c314f 100644 --- a/mocks/persistence/events_store.go +++ b/mocks/persistence/events_store.go @@ -26,6 +26,10 @@ func (_m *EventsStore) EXPECT() *EventsStore_Expecter { func (_m *EventsStore) Connect(ctx context.Context) error { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for Connect") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) @@ -68,6 +72,10 @@ func (_c *EventsStore_Connect_Call) RunAndReturn(run func(context.Context) error func (_m *EventsStore) DeleteEvents(ctx context.Context, persistenceID string, toSequenceNumber uint64) error { ret := _m.Called(ctx, persistenceID, toSequenceNumber) + if len(ret) == 0 { + panic("no return value specified for DeleteEvents") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, uint64) error); ok { r0 = rf(ctx, persistenceID, toSequenceNumber) @@ -112,6 +120,10 @@ func (_c *EventsStore_DeleteEvents_Call) RunAndReturn(run func(context.Context, func (_m *EventsStore) Disconnect(ctx context.Context) error { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for Disconnect") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) @@ -154,6 +166,10 @@ func (_c *EventsStore_Disconnect_Call) RunAndReturn(run func(context.Context) er func (_m *EventsStore) GetLatestEvent(ctx context.Context, persistenceID string) (*egopb.Event, error) { ret := _m.Called(ctx, persistenceID) + if len(ret) == 0 { + panic("no return value specified for GetLatestEvent") + } + var r0 *egopb.Event var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (*egopb.Event, error)); ok { @@ -205,18 +221,22 @@ func (_c *EventsStore_GetLatestEvent_Call) RunAndReturn(run func(context.Context return _c } -// GetShardEvents provides a mock function with given fields: ctx, shardNumber, offset, max -func (_m *EventsStore) GetShardEvents(ctx context.Context, shardNumber uint64, offset int64, max uint64) ([]*egopb.Event, int64, error) { - ret := _m.Called(ctx, shardNumber, offset, max) +// GetShardEvents provides a mock function with given fields: ctx, shardNumber, offset, limit +func (_m *EventsStore) GetShardEvents(ctx context.Context, shardNumber uint64, offset int64, limit uint64) ([]*egopb.Event, int64, error) { + ret := _m.Called(ctx, shardNumber, offset, limit) + + if len(ret) == 0 { + panic("no return value specified for GetShardEvents") + } var r0 []*egopb.Event var r1 int64 var r2 error if rf, ok := ret.Get(0).(func(context.Context, uint64, int64, uint64) ([]*egopb.Event, int64, error)); ok { - return rf(ctx, shardNumber, offset, max) + return rf(ctx, shardNumber, offset, limit) } if rf, ok := ret.Get(0).(func(context.Context, uint64, int64, uint64) []*egopb.Event); ok { - r0 = rf(ctx, shardNumber, offset, max) + r0 = rf(ctx, shardNumber, offset, limit) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]*egopb.Event) @@ -224,13 +244,13 @@ func (_m *EventsStore) GetShardEvents(ctx context.Context, shardNumber uint64, o } if rf, ok := ret.Get(1).(func(context.Context, uint64, int64, uint64) int64); ok { - r1 = rf(ctx, shardNumber, offset, max) + r1 = rf(ctx, shardNumber, offset, limit) } else { r1 = ret.Get(1).(int64) } if rf, ok := ret.Get(2).(func(context.Context, uint64, int64, uint64) error); ok { - r2 = rf(ctx, shardNumber, offset, max) + r2 = rf(ctx, shardNumber, offset, limit) } else { r2 = ret.Error(2) } @@ -247,12 +267,12 @@ type EventsStore_GetShardEvents_Call struct { // - ctx context.Context // - shardNumber uint64 // - offset int64 -// - max uint64 -func (_e *EventsStore_Expecter) GetShardEvents(ctx interface{}, shardNumber interface{}, offset interface{}, max interface{}) *EventsStore_GetShardEvents_Call { - return &EventsStore_GetShardEvents_Call{Call: _e.mock.On("GetShardEvents", ctx, shardNumber, offset, max)} +// - limit uint64 +func (_e *EventsStore_Expecter) GetShardEvents(ctx interface{}, shardNumber interface{}, offset interface{}, limit interface{}) *EventsStore_GetShardEvents_Call { + return &EventsStore_GetShardEvents_Call{Call: _e.mock.On("GetShardEvents", ctx, shardNumber, offset, limit)} } -func (_c *EventsStore_GetShardEvents_Call) Run(run func(ctx context.Context, shardNumber uint64, offset int64, max uint64)) *EventsStore_GetShardEvents_Call { +func (_c *EventsStore_GetShardEvents_Call) Run(run func(ctx context.Context, shardNumber uint64, offset int64, limit uint64)) *EventsStore_GetShardEvents_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context), args[1].(uint64), args[2].(int64), args[3].(uint64)) }) @@ -273,6 +293,10 @@ func (_c *EventsStore_GetShardEvents_Call) RunAndReturn(run func(context.Context func (_m *EventsStore) PersistenceIDs(ctx context.Context, pageSize uint64, pageToken string) ([]string, string, error) { ret := _m.Called(ctx, pageSize, pageToken) + if len(ret) == 0 { + panic("no return value specified for PersistenceIDs") + } + var r0 []string var r1 string var r2 error @@ -336,6 +360,10 @@ func (_c *EventsStore_PersistenceIDs_Call) RunAndReturn(run func(context.Context func (_m *EventsStore) Ping(ctx context.Context) error { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for Ping") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) @@ -374,17 +402,21 @@ func (_c *EventsStore_Ping_Call) RunAndReturn(run func(context.Context) error) * return _c } -// ReplayEvents provides a mock function with given fields: ctx, persistenceID, fromSequenceNumber, toSequenceNumber, max -func (_m *EventsStore) ReplayEvents(ctx context.Context, persistenceID string, fromSequenceNumber uint64, toSequenceNumber uint64, max uint64) ([]*egopb.Event, error) { - ret := _m.Called(ctx, persistenceID, fromSequenceNumber, toSequenceNumber, max) +// ReplayEvents provides a mock function with given fields: ctx, persistenceID, fromSequenceNumber, toSequenceNumber, limit +func (_m *EventsStore) ReplayEvents(ctx context.Context, persistenceID string, fromSequenceNumber uint64, toSequenceNumber uint64, limit uint64) ([]*egopb.Event, error) { + ret := _m.Called(ctx, persistenceID, fromSequenceNumber, toSequenceNumber, limit) + + if len(ret) == 0 { + panic("no return value specified for ReplayEvents") + } var r0 []*egopb.Event var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, uint64, uint64, uint64) ([]*egopb.Event, error)); ok { - return rf(ctx, persistenceID, fromSequenceNumber, toSequenceNumber, max) + return rf(ctx, persistenceID, fromSequenceNumber, toSequenceNumber, limit) } if rf, ok := ret.Get(0).(func(context.Context, string, uint64, uint64, uint64) []*egopb.Event); ok { - r0 = rf(ctx, persistenceID, fromSequenceNumber, toSequenceNumber, max) + r0 = rf(ctx, persistenceID, fromSequenceNumber, toSequenceNumber, limit) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]*egopb.Event) @@ -392,7 +424,7 @@ func (_m *EventsStore) ReplayEvents(ctx context.Context, persistenceID string, f } if rf, ok := ret.Get(1).(func(context.Context, string, uint64, uint64, uint64) error); ok { - r1 = rf(ctx, persistenceID, fromSequenceNumber, toSequenceNumber, max) + r1 = rf(ctx, persistenceID, fromSequenceNumber, toSequenceNumber, limit) } else { r1 = ret.Error(1) } @@ -410,12 +442,12 @@ type EventsStore_ReplayEvents_Call struct { // - persistenceID string // - fromSequenceNumber uint64 // - toSequenceNumber uint64 -// - max uint64 -func (_e *EventsStore_Expecter) ReplayEvents(ctx interface{}, persistenceID interface{}, fromSequenceNumber interface{}, toSequenceNumber interface{}, max interface{}) *EventsStore_ReplayEvents_Call { - return &EventsStore_ReplayEvents_Call{Call: _e.mock.On("ReplayEvents", ctx, persistenceID, fromSequenceNumber, toSequenceNumber, max)} +// - limit uint64 +func (_e *EventsStore_Expecter) ReplayEvents(ctx interface{}, persistenceID interface{}, fromSequenceNumber interface{}, toSequenceNumber interface{}, limit interface{}) *EventsStore_ReplayEvents_Call { + return &EventsStore_ReplayEvents_Call{Call: _e.mock.On("ReplayEvents", ctx, persistenceID, fromSequenceNumber, toSequenceNumber, limit)} } -func (_c *EventsStore_ReplayEvents_Call) Run(run func(ctx context.Context, persistenceID string, fromSequenceNumber uint64, toSequenceNumber uint64, max uint64)) *EventsStore_ReplayEvents_Call { +func (_c *EventsStore_ReplayEvents_Call) Run(run func(ctx context.Context, persistenceID string, fromSequenceNumber uint64, toSequenceNumber uint64, limit uint64)) *EventsStore_ReplayEvents_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context), args[1].(string), args[2].(uint64), args[3].(uint64), args[4].(uint64)) }) @@ -436,6 +468,10 @@ func (_c *EventsStore_ReplayEvents_Call) RunAndReturn(run func(context.Context, func (_m *EventsStore) ShardNumbers(ctx context.Context) ([]uint64, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for ShardNumbers") + } + var r0 []uint64 var r1 error if rf, ok := ret.Get(0).(func(context.Context) ([]uint64, error)); ok { @@ -490,6 +526,10 @@ func (_c *EventsStore_ShardNumbers_Call) RunAndReturn(run func(context.Context) func (_m *EventsStore) WriteEvents(ctx context.Context, events []*egopb.Event) error { ret := _m.Called(ctx, events) + if len(ret) == 0 { + panic("no return value specified for WriteEvents") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, []*egopb.Event) error); ok { r0 = rf(ctx, events) diff --git a/mocks/persistence/state_store.go b/mocks/persistence/state_store.go index 1bf39dd..78ba697 100644 --- a/mocks/persistence/state_store.go +++ b/mocks/persistence/state_store.go @@ -26,6 +26,10 @@ func (_m *StateStore) EXPECT() *StateStore_Expecter { func (_m *StateStore) Connect(ctx context.Context) error { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for Connect") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) @@ -68,6 +72,10 @@ func (_c *StateStore_Connect_Call) RunAndReturn(run func(context.Context) error) func (_m *StateStore) Disconnect(ctx context.Context) error { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for Disconnect") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) @@ -110,6 +118,10 @@ func (_c *StateStore_Disconnect_Call) RunAndReturn(run func(context.Context) err func (_m *StateStore) GetLatestState(ctx context.Context, persistenceID string) (*egopb.DurableState, error) { ret := _m.Called(ctx, persistenceID) + if len(ret) == 0 { + panic("no return value specified for GetLatestState") + } + var r0 *egopb.DurableState var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (*egopb.DurableState, error)); ok { @@ -165,6 +177,10 @@ func (_c *StateStore_GetLatestState_Call) RunAndReturn(run func(context.Context, func (_m *StateStore) Ping(ctx context.Context) error { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for Ping") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) @@ -207,6 +223,10 @@ func (_c *StateStore_Ping_Call) RunAndReturn(run func(context.Context) error) *S func (_m *StateStore) WriteState(ctx context.Context, state *egopb.DurableState) error { ret := _m.Called(ctx, state) + if len(ret) == 0 { + panic("no return value specified for WriteState") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *egopb.DurableState) error); ok { r0 = rf(ctx, state) diff --git a/offsetstore/offset_store.go b/offsetstore/offset_store.go index 240a322..75bfeb9 100644 --- a/offsetstore/offset_store.go +++ b/offsetstore/offset_store.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/option.go b/option.go index 0fcbd06..334beb9 100644 --- a/option.go +++ b/option.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,13 +27,15 @@ package ego import ( "go.uber.org/atomic" - "github.com/tochemey/goakt/v2/discovery" - "github.com/tochemey/goakt/v2/log" + "github.com/tochemey/goakt/v3/discovery" + "github.com/tochemey/goakt/v3/log" "github.com/tochemey/ego/v3/persistence" ) -// Option is the interface that applies a configuration option. +// Option defines a configuration option that can be applied to a Engine. +// +// Implementations of this interface modify the Engine's configuration when applied. type Option interface { // Apply sets the Option value of a config. Apply(e *Engine) @@ -41,38 +43,91 @@ type Option interface { var _ Option = OptionFunc(nil) -// OptionFunc implements the Option interface. +// OptionFunc is a function type that implements the Option interface. +// +// It allows functions to be used as configuration options for Engine. type OptionFunc func(e *Engine) -// Apply applies the options to Engine +// Apply applies the OptionFunc to the given Engine. +// +// This enables the use of functions as dynamic configuration options. func (f OptionFunc) Apply(e *Engine) { f(e) } -// WithCluster enables cluster mode -func WithCluster(provider discovery.Provider, partitionCount uint64, minimumPeersQuorum uint16, host string, remotingPort, gossipPort, peersPort int) Option { +// WithCluster enables cluster mode by configuring the necessary parameters +// for distributed communication and peer discovery. +// +// Parameters: +// - provider: The discovery.Provider responsible for peers discovery in the cluster. +// - partitionCount: The number of partitions used for distributing data across the cluster. +// - minimumPeersQuorum: The minimum number of peers required to form a quorum. +// - host: The hostname or IP address of the current node. +// - remotingPort: The port used for remote actor communication. +// - discoveryPort: The port used for service discovery. +// - peersPort: The port used for peer-to-peer communication. +// +// Returns: +// - Option: A functional option that configures the cluster settings. +func WithCluster(provider discovery.Provider, partitionCount uint64, minimumPeersQuorum uint16, host string, remotingPort, discoveryPort, peersPort int) Option { return OptionFunc(func(e *Engine) { e.enableCluster = atomic.NewBool(true) e.discoveryProvider = provider e.partitionsCount = partitionCount e.peersPort = peersPort e.minimumPeersQuorum = minimumPeersQuorum - e.gossipPort = gossipPort - e.hostName = host + e.discoveryPort = discoveryPort + e.bindAddr = host e.remotingPort = remotingPort }) } -// WithLogger sets the logger +// WithLogger sets the logger for the system, allowing custom logging implementations. +// +// Parameters: +// - logger: An instance of log.Logger used for logging system events and debugging information. +// +// Returns: +// - Option: A functional option that configures the logger. func WithLogger(logger log.Logger) Option { return OptionFunc(func(e *Engine) { e.logger = logger }) } -// WithStateStore sets the durable store. This is necessary when creating a durable state entity +// WithStateStore sets the durable state store for persisting entity state. +// This is necessary when creating a durable state entity to ensure state +// survives restarts and failures. +// +// Parameters: +// - stateStore: An instance of persistence.StateStore responsible for storing entity state durably. +// +// Returns: +// - Option: A functional option that configures the state store. func WithStateStore(stateStore persistence.StateStore) Option { return OptionFunc(func(e *Engine) { e.stateStore = stateStore }) } + +// WithTLS configures TLS settings for both the server and client, ensuring +// secure communication through encryption and authentication. +// +// Ensure that both the server and client are configured with the same +// root Certificate Authority (CA) to enable a successful handshake and +// mutual authentication. +// +// In cluster mode, all nodes must share the same root CA to establish +// secure communication and complete handshakes successfully. +// +// Parameters: +// - tls: A pointer to a TLS configuration struct that contains the +// client and server TLS settings. +// +// Returns: +// - Option: A functional option that configures TLS settings. +func WithTLS(tls *TLS) Option { + return OptionFunc(func(e *Engine) { + e.tls = tls + }) +} diff --git a/option_test.go b/option_test.go index 39321ce..52b4ecc 100644 --- a/option_test.go +++ b/option_test.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,8 +30,8 @@ import ( "github.com/stretchr/testify/assert" "go.uber.org/atomic" - "github.com/tochemey/goakt/v2/discovery/kubernetes" - "github.com/tochemey/goakt/v2/log" + "github.com/tochemey/goakt/v3/discovery/kubernetes" + "github.com/tochemey/goakt/v3/log" ) func TestOptions(t *testing.T) { @@ -51,8 +51,8 @@ func TestOptions(t *testing.T) { expected: Engine{ discoveryProvider: discoveryProvider, minimumPeersQuorum: 3, - hostName: "localhost", - gossipPort: 1335, + bindAddr: "localhost", + discoveryPort: 1335, peersPort: 1336, remotingPort: 1334, partitionsCount: 30, diff --git a/persistence/events_store.go b/persistence/events_store.go index 3c3807c..501df94 100644 --- a/persistence/events_store.go +++ b/persistence/events_store.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -45,13 +45,13 @@ type EventsStore interface { // DeleteEvents deletes store from the store up to a given sequence number (inclusive) DeleteEvents(ctx context.Context, persistenceID string, toSequenceNumber uint64) error // ReplayEvents fetches store for a given persistence ID from a given sequence number(inclusive) to a given sequence number(inclusive) with a maximum of journals to be replayed. - ReplayEvents(ctx context.Context, persistenceID string, fromSequenceNumber, toSequenceNumber uint64, max uint64) ([]*egopb.Event, error) + ReplayEvents(ctx context.Context, persistenceID string, fromSequenceNumber, toSequenceNumber uint64, limit uint64) ([]*egopb.Event, error) // GetLatestEvent fetches the latest event GetLatestEvent(ctx context.Context, persistenceID string) (*egopb.Event, error) // PersistenceIDs returns the distinct list of all the persistence ids in the journal store PersistenceIDs(ctx context.Context, pageSize uint64, pageToken string) (persistenceIDs []string, nextPageToken string, err error) - // GetShardEvents returns the next (max) events after the offset in the journal for a given shard - GetShardEvents(ctx context.Context, shardNumber uint64, offset int64, max uint64) ([]*egopb.Event, int64, error) + // GetShardEvents returns the next (limit) events after the offset in the journal for a given shard + GetShardEvents(ctx context.Context, shardNumber uint64, offset int64, limit uint64) ([]*egopb.Event, int64, error) // ShardNumbers returns the distinct list of all the shards in the journal store ShardNumbers(ctx context.Context) ([]uint64, error) } diff --git a/persistence/state_store.go b/persistence/state_store.go index 722fd62..50bf61d 100644 --- a/persistence/state_store.go +++ b/persistence/state_store.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/projection/handler.go b/projection/handler.go index 5930631..0b0be36 100644 --- a/projection/handler.go +++ b/projection/handler.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,7 +30,7 @@ import ( "go.uber.org/atomic" "google.golang.org/protobuf/types/known/anypb" - "github.com/tochemey/goakt/v2/log" + "github.com/tochemey/goakt/v3/log" ) // Handler is used to handle event and state consumed from the event store diff --git a/projection/option.go b/projection/option.go index 5180bf3..9e5c6a5 100644 --- a/projection/option.go +++ b/projection/option.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,7 +27,7 @@ package projection import ( "time" - "github.com/tochemey/goakt/v2/log" + "github.com/tochemey/goakt/v3/log" ) // Option is the interface that applies a configuration option. diff --git a/projection/option_test.go b/projection/option_test.go index 0e66bb2..2a8a294 100644 --- a/projection/option_test.go +++ b/projection/option_test.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,7 +30,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/tochemey/goakt/v2/log" + "github.com/tochemey/goakt/v3/log" ) func TestOption(t *testing.T) { diff --git a/projection/projection.go b/projection/projection.go index bb66154..6b2b1b0 100644 --- a/projection/projection.go +++ b/projection/projection.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,8 +27,8 @@ package projection import ( "context" - "github.com/tochemey/goakt/v2/actors" - "github.com/tochemey/goakt/v2/goaktpb" + goakt "github.com/tochemey/goakt/v3/actor" + "github.com/tochemey/goakt/v3/goaktpb" "github.com/tochemey/ego/v3/offsetstore" "github.com/tochemey/ego/v3/persistence" @@ -41,7 +41,7 @@ type Projection struct { } // implements the Actor contract -var _ actors.Actor = (*Projection)(nil) +var _ goakt.Actor = (*Projection)(nil) // New creates an instance of Projection func New(name string, @@ -60,7 +60,7 @@ func (proj *Projection) PreStart(ctx context.Context) error { } // Receive handle the message sent to the projection actor -func (proj *Projection) Receive(ctx *actors.ReceiveContext) { +func (proj *Projection) Receive(ctx *goakt.ReceiveContext) { switch ctx.Message().(type) { case *goaktpb.PostStart: proj.runner.Run(ctx.Context()) diff --git a/projection/projection_test.go b/projection/projection_test.go index 502a6a3..6e0de6c 100644 --- a/projection/projection_test.go +++ b/projection/projection_test.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -32,8 +32,8 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tochemey/goakt/v2/actors" - "github.com/tochemey/goakt/v2/log" + goakt "github.com/tochemey/goakt/v3/actor" + "github.com/tochemey/goakt/v3/log" "go.uber.org/goleak" "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/timestamppb" @@ -50,10 +50,10 @@ func TestProjection(t *testing.T) { ctx := context.TODO() logger := log.DiscardLogger // create an actor system - actorSystem, err := actors.NewActorSystem("TestActorSystem", - actors.WithPassivationDisabled(), - actors.WithLogger(logger), - actors.WithActorInitMaxRetries(3)) + actorSystem, err := goakt.NewActorSystem("TestActorSystem", + goakt.WithPassivationDisabled(), + goakt.WithLogger(logger), + goakt.WithActorInitMaxRetries(3)) require.NoError(t, err) assert.NotNil(t, actorSystem) diff --git a/projection/recovery.go b/projection/recovery.go index 51a5f5f..e7a0ad9 100644 --- a/projection/recovery.go +++ b/projection/recovery.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/projection/recovery_test.go b/projection/recovery_test.go index 6937fbd..d34e854 100644 --- a/projection/recovery_test.go +++ b/projection/recovery_test.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/projection/runner.go b/projection/runner.go index 4c24ef7..7f27f09 100644 --- a/projection/runner.go +++ b/projection/runner.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -32,12 +32,11 @@ import ( "time" "github.com/flowchartsman/retry" + "github.com/tochemey/goakt/v3/log" "go.uber.org/atomic" "golang.org/x/sync/errgroup" "google.golang.org/protobuf/types/known/timestamppb" - "github.com/tochemey/goakt/v2/log" - "github.com/tochemey/ego/v3/egopb" "github.com/tochemey/ego/v3/internal/ticker" "github.com/tochemey/ego/v3/offsetstore" diff --git a/projection/runner_test.go b/projection/runner_test.go index 76dd6ee..57ec5c8 100644 --- a/projection/runner_test.go +++ b/projection/runner_test.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -34,7 +34,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/tochemey/goakt/v2/log" + "github.com/tochemey/goakt/v3/log" "go.uber.org/atomic" "go.uber.org/goleak" "google.golang.org/protobuf/types/known/anypb" diff --git a/publisher.go b/publisher.go new file mode 100644 index 0000000..021e847 --- /dev/null +++ b/publisher.go @@ -0,0 +1,105 @@ +/* + * MIT License + * + * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package ego + +import ( + "context" + "errors" + + "github.com/tochemey/ego/v3/egopb" +) + +// ErrPublisherNotStarted is returned when an operation is attempted on a publisher that has not been started. +var ErrPublisherNotStarted = errors.New("publisher not started") + +// EventPublisher defines an interface for publishing events. +// Implementations of this interface are responsible for delivering events +// to the appropriate event stream, message broker, or event log. +type EventPublisher interface { + // ID returns the unique identifier for the event publisher. + // This identifier is used to distinguish between different publishers. + // + // Returns: + // - string: The unique identifier for the event publisher. + ID() string + + // Publish delivers an event to the event stream or message broker. + // It requires a context for cancellation and timeouts, and the event + // to be published. Implementations of this method are responsible for + // ensuring that the event is successfully delivered. + // + // Parameters: + // - ctx: The context for managing cancellation and timeouts. + // - event: The event to be published. + // + // Returns: + // - error: If an error occurs during event publication, it is returned. + Publish(ctx context.Context, event *egopb.Event) error + + // Close closes the event publisher. + // This method is called when the publisher is being shut down. + // It should be used to clean up any resources used by the publisher. + // + // Parameters: + // - ctx: The context for managing cancellation and timeouts. + // + // Returns: + // - error: If an error occurs during close, it is returned. + Close(context.Context) error +} + +// StatePublisher defines an interface for publishing durable state changes. +// This is typically used to persist entity state updates in a distributed system. +type StatePublisher interface { + // ID returns the unique identifier for the state publisher. + // This identifier is used to distinguish between different publishers. + // + // Returns: + // - string: The unique identifier for the state publisher. + ID() string + + // Publish delivers a durable state update to the state store. + // The provided state will be persisted and should be used to + // ensure consistency across nodes in a distributed system. + // + // Parameters: + // - ctx: The context for managing cancellation and timeouts. + // - state: The durable state to be published. + // + // Returns: + // - error: If an error occurs during state publication, it is returned. + Publish(ctx context.Context, state *egopb.DurableState) error + + // Close closes the durbable state publisher. + // This method is called when the publisher is being shut down. + // It should be used to clean up any resources used by the publisher. + // + // Parameters: + // - ctx: The context for managing cancellation and timeouts. + // + // Returns: + // - error: If an error occurs during close, it is returned. + Close(context.Context) error +} diff --git a/publisher/kafka/config.go b/publisher/kafka/config.go new file mode 100644 index 0000000..50d5062 --- /dev/null +++ b/publisher/kafka/config.go @@ -0,0 +1,91 @@ +/* + * MIT License + * + * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package kafka + +import ( + "crypto/tls" + "time" + + "github.com/IBM/sarama" + "github.com/tochemey/goakt/v3/log" +) + +// Config is a set of base config values required for connecting to Kafka +type Config struct { + // Brokers is a list of Kafka broker addresses. + Brokers []string + // EnableTLS indicates whether TLS should be enabled for the connection. + EnableTLS bool + // APIKey is the API key for SASL authentication. + APIKey string + // APISecret is the API secret for SASL authentication. + APISecret string + // KeepAlive specifies the keep-alive period for the connection. + KeepAlive time.Duration + // TLS is the TLS configuration for the connection. + TLS *tls.Config + // MaxRetries is the maximum number of retries for sending a message. + MaxRetries int + // RetryInterval is the interval between retries. + RetryInterval time.Duration + // EventsTopic is the topic for publishing events. + EventsTopic string + // StateTopic is the topic for publishing state changes. + StateTopic string + // Logger is the logger for the publisher. + Logger log.Logger +} + +func toSaramaConfig(config *Config) *sarama.Config { + saramaConfig := sarama.NewConfig() + saramaConfig.Version = sarama.V0_11_0_2 + saramaConfig.ClientID = "ego-kafka-publisher" + + if config.EnableTLS { + saramaConfig.Net.TLS.Enable = true + saramaConfig.Net.TLS.Config = config.TLS + saramaConfig.Net.TLS.Config.ServerName = config.Brokers[0] + } + + if config.APIKey != "" && config.APISecret != "" { + saramaConfig.Net.SASL.Enable = true + saramaConfig.Net.SASL.User = config.APIKey + saramaConfig.Net.SASL.Password = config.APISecret + saramaConfig.Net.SASL.Mechanism = sarama.SASLTypePlaintext + } + + saramaConfig.Net.KeepAlive = config.KeepAlive + // producer settings + saramaConfig.Producer.Idempotent = true + saramaConfig.Net.MaxOpenRequests = 1 + saramaConfig.Producer.Return.Successes = true + saramaConfig.Producer.Return.Errors = true + saramaConfig.Producer.RequiredAcks = sarama.WaitForAll + saramaConfig.Producer.Retry.Max = config.MaxRetries + saramaConfig.Producer.Retry.Backoff = config.RetryInterval + saramaConfig.Producer.Partitioner = sarama.NewHashPartitioner + + return saramaConfig +} diff --git a/publisher/kafka/doc.go b/publisher/kafka/doc.go new file mode 100644 index 0000000..ff0076a --- /dev/null +++ b/publisher/kafka/doc.go @@ -0,0 +1,26 @@ +/* + * MIT License + * + * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// Package kafka implements the Apache Kafka connector. +package kafka diff --git a/publisher/kafka/go.mod b/publisher/kafka/go.mod new file mode 100644 index 0000000..f4d4875 --- /dev/null +++ b/publisher/kafka/go.mod @@ -0,0 +1,76 @@ +module github.com/tochemey/ego/v3/publisher/kafka + +go 1.23.0 + +require ( + github.com/IBM/sarama v1.45.0 + github.com/tochemey/ego/v3 v3.3.3-0.20250216200937-22de818f78ec + go.uber.org/atomic v1.11.0 + google.golang.org/protobuf v1.36.5 +) + +require ( + connectrpc.com/connect v1.18.1 // indirect + github.com/RoaringBitmap/roaring v1.9.4 // indirect + github.com/Workiva/go-datastructures v1.1.5 // indirect + github.com/armon/go-metrics v0.4.1 // indirect + github.com/bits-and-blooms/bitset v1.20.0 // indirect + github.com/buraksezer/consistent v0.10.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/deckarep/golang-set/v2 v2.7.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/flowchartsman/retry v1.2.0 // indirect + github.com/google/btree v1.1.3 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-metrics v0.5.4 // indirect + github.com/hashicorp/go-msgpack/v2 v2.1.2 // indirect + github.com/hashicorp/go-sockaddr v1.0.7 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/hashicorp/logutils v1.0.0 // indirect + github.com/hashicorp/memberlist v0.5.3 // indirect + github.com/klauspost/cpuid/v2 v2.2.9 // indirect + github.com/miekg/dns v1.1.63 // indirect + github.com/mschoch/smat v0.2.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/redis/go-redis/v9 v9.7.0 // indirect + github.com/reugn/go-quartz v0.14.0 // indirect + github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/tidwall/btree v1.7.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/redcon v1.6.2 // indirect + github.com/tochemey/olric v0.2.0 // indirect + github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + github.com/zeebo/xxh3 v1.0.2 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/mod v0.23.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect + golang.org/x/tools v0.30.0 // indirect +) + +require ( + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/eapache/go-resiliency v1.7.0 // indirect + github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 // indirect + github.com/eapache/queue v1.1.0 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-uuid v1.0.3 // indirect + github.com/jcmturner/aescts/v2 v2.0.0 // indirect + github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect + github.com/jcmturner/gofork v1.7.6 // indirect + github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect + github.com/jcmturner/rpc/v2 v2.0.3 // indirect + github.com/klauspost/compress v1.17.11 // indirect + github.com/pierrec/lz4/v4 v4.1.22 // indirect + github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect + github.com/tochemey/goakt/v3 v3.0.0 + golang.org/x/crypto v0.33.0 // indirect + golang.org/x/net v0.35.0 // indirect +) diff --git a/publisher/kafka/go.sum b/publisher/kafka/go.sum new file mode 100644 index 0000000..8b3637e --- /dev/null +++ b/publisher/kafka/go.sum @@ -0,0 +1,419 @@ +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= +connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/IBM/sarama v1.45.0 h1:IzeBevTn809IJ/dhNKhP5mpxEXTmELuezO2tgHD9G5E= +github.com/IBM/sarama v1.45.0/go.mod h1:EEay63m8EZkeumco9TDXf2JT3uDnZsZqFgV46n4yZdY= +github.com/RoaringBitmap/roaring v1.9.4 h1:yhEIoH4YezLYT04s1nHehNO64EKFTop/wBhxv2QzDdQ= +github.com/RoaringBitmap/roaring v1.9.4/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90= +github.com/Workiva/go-datastructures v1.1.5 h1:5YfhQ4ry7bZc2Mc7R0YZyYwpf5c6t1cEFvdAhd6Mkf4= +github.com/Workiva/go-datastructures v1.1.5/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= +github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU= +github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/buraksezer/consistent v0.10.0 h1:hqBgz1PvNLC5rkWcEBVAL9dFMBWz6I0VgUCW25rrZlU= +github.com/buraksezer/consistent v0.10.0/go.mod h1:6BrVajWq7wbKZlTOUPs/XVfR8c0maujuPowduSpZqmw= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set/v2 v2.7.0 h1:gIloKvD7yH2oip4VLhsv3JyLLFnC0Y2mlusgcvJYW5k= +github.com/deckarep/golang-set/v2 v2.7.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/eapache/go-resiliency v1.7.0 h1:n3NRTnBn5N0Cbi/IeOHuQn9s2UwVUH7Ga0ZWcP+9JTA= +github.com/eapache/go-resiliency v1.7.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= +github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 h1:Oy0F4ALJ04o5Qqpdz8XLIpNA3WM/iSIXqxtqo7UGVws= +github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3/go.mod h1:YvSRo5mw33fLEx1+DlK6L2VV43tJt5Eyel9n9XBcR+0= +github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= +github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/flowchartsman/retry v1.2.0 h1:qDhlw6RNufXz6RGr+IiYimFpMMkt77SUSHY5tgFaUCU= +github.com/flowchartsman/retry v1.2.0/go.mod h1:+sfx8OgCCiAr3t5jh2Gk+T0fRTI+k52edaYxURQxY64= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-metrics v0.5.4 h1:8mmPiIJkTPPEbAiV97IxdAGNdRdaWwVap1BU6elejKY= +github.com/hashicorp/go-metrics v0.5.4/go.mod h1:CG5yz4NZ/AI/aQt9Ucm/vdBnbh7fvmv4lxZ350i+QQI= +github.com/hashicorp/go-msgpack/v2 v2.1.2 h1:4Ee8FTp834e+ewB71RDrQ0VKpyFdrKOjvYtnQ/ltVj0= +github.com/hashicorp/go-msgpack/v2 v2.1.2/go.mod h1:upybraOAblm4S7rx0+jeNy+CWWhzywQsSRV5033mMu4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw= +github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/memberlist v0.5.3 h1:tQ1jOCypD0WvMemw/ZhhtH+PWpzcftQvgCorLu0hndk= +github.com/hashicorp/memberlist v0.5.3/go.mod h1:h60o12SZn/ua/j0B6iKAZezA4eDaGsIuPO70eOaJ6WE= +github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= +github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= +github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= +github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= +github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg= +github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= +github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o= +github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= +github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8= +github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= +github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= +github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kapetan-io/tackle v0.11.0 h1:xcQ2WgES8rjsd0ZMBfFTMuCs8YG4+1r2OAPY0+mHXjM= +github.com/kapetan-io/tackle v0.11.0/go.mod h1:94m0H3j8pm9JMsAuqBsC/Y08WpAUh01ugkFxABjjHd8= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= +github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= +github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY= +github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs= +github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q= +github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= +github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nats-io/jwt/v2 v2.7.3 h1:6bNPK+FXgBeAqdj4cYQ0F8ViHRbi7woQLq4W29nUAzE= +github.com/nats-io/jwt/v2 v2.7.3/go.mod h1:GvkcbHhKquj3pkioy5put1wvPxs78UlZ7D/pY+BgZk4= +github.com/nats-io/nats-server/v2 v2.10.25 h1:J0GWLDDXo5HId7ti/lTmBfs+lzhmu8RPkoKl0eSCqwc= +github.com/nats-io/nats-server/v2 v2.10.25/go.mod h1:/YYYQO7cuoOBt+A7/8cVjuhWTaTUEAlZbJT+3sMAfFU= +github.com/nats-io/nats.go v1.39.0 h1:2/yg2JQjiYYKLwDuBzV0FbB2sIV+eFNkEevlRi4n9lI= +github.com/nats-io/nats.go v1.39.0/go.mod h1:MgRb8oOdigA6cYpEPhXJuRVH6UE/V4jblJ2jQ27IXYM= +github.com/nats-io/nkeys v0.4.9 h1:qe9Faq2Gxwi6RZnZMXfmGMZkg3afLLOtrU+gDZJ35b0= +github.com/nats-io/nkeys v0.4.9/go.mod h1:jcMqs+FLG+W5YO36OX6wFIFcmpdAns+w1Wm6D3I/evE= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= +github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= +github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= +github.com/reugn/go-quartz v0.14.0 h1:KlIBAsOIw1JI8Rc7/f8VrrHBHOr+BiqrTiB35pRe84M= +github.com/reugn/go-quartz v0.14.0/go.mod h1:00DVnBKq2Fxag/HlR9mGXjmHNlMFQ1n/LNM+Fn0jUaE= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tidwall/btree v1.1.0/go.mod h1:TzIRzen6yHbibdSfK6t8QimqbUnoxUSrZfeW7Uob0q4= +github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= +github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/redcon v1.6.2 h1:5qfvrrybgtO85jnhSravmkZyC0D+7WstbfCs3MmPhow= +github.com/tidwall/redcon v1.6.2/go.mod h1:p5Wbsgeyi2VSTBWOcA5vRXrOb9arFTcU2+ZzFjqV75Y= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/tochemey/ego/v3 v3.3.3-0.20250216200937-22de818f78ec h1:SCawI23m5Jepuz1Br07p70Hl0+XKkWubioIj90NVW9s= +github.com/tochemey/ego/v3 v3.3.3-0.20250216200937-22de818f78ec/go.mod h1:TuowN3LV6YVItwS4MZlyZ51oZBd75XIylYn0XwKSETM= +github.com/tochemey/goakt/v3 v3.0.0 h1:WukWOsXbaCkIjXWKm0GHY81s6yKeHDE06Vj3O42cJ4c= +github.com/tochemey/goakt/v3 v3.0.0/go.mod h1:I3F45cKjyQiWnTqY4PLcnM9hBYhzb3vjulUoD9ComNc= +github.com/tochemey/olric v0.2.0 h1:y/uoVNNVdMn+pbUI01i6TqeD48+VgjRcoRGOzVzkdVY= +github.com/tochemey/olric v0.2.0/go.mod h1:vRpB3Xb0L4J7acugeAfrIXcwLDlCwRsC10s1jCYND1Y= +github.com/travisjeffery/go-dynaport v1.0.0 h1:m/qqf5AHgB96CMMSworIPyo1i7NZueRsnwdzdCJ8Ajw= +github.com/travisjeffery/go-dynaport v1.0.0/go.mod h1:0LHuDS4QAx+mAc4ri3WkQdavgVoBIZ7cE9ob17KIAJk= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= +golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= +golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.32.2 h1:bZrMLEkgizC24G9eViHGOPbW+aRo9duEISRIJKfdJuw= +k8s.io/api v0.32.2/go.mod h1:hKlhk4x1sJyYnHENsrdCWw31FEmCijNGPJO5WzHiJ6Y= +k8s.io/apimachinery v0.32.2 h1:yoQBR9ZGkA6Rgmhbp/yuT9/g+4lxtsGYwW6dR6BDPLQ= +k8s.io/apimachinery v0.32.2/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/client-go v0.32.2 h1:4dYCD4Nz+9RApM2b/3BtVvBHw54QjMFUl1OLcJG5yOA= +k8s.io/client-go v0.32.2/go.mod h1:fpZ4oJXclZ3r2nDOv+Ux3XcJutfrwjKTCHz2H3sww94= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg= +k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas= +k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= +k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk= +sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/publisher/kafka/kafka.go b/publisher/kafka/kafka.go new file mode 100644 index 0000000..57f4bad --- /dev/null +++ b/publisher/kafka/kafka.go @@ -0,0 +1,197 @@ +/* + * MIT License + * + * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package kafka + +import ( + "context" + "time" + + "github.com/IBM/sarama" + "go.uber.org/atomic" + "google.golang.org/protobuf/proto" + + "github.com/tochemey/ego/v3" + "github.com/tochemey/ego/v3/egopb" +) + +// EventsPublisher defines a Kafka publisher. +// This publisher is responsible for delivering ego events to a Kafka broker. +type EventsPublisher struct { + config *Config + producer sarama.SyncProducer + started *atomic.Bool +} + +// ensure EventsPublisher implements ego.EventPublisher. +var _ ego.EventPublisher = (*EventsPublisher)(nil) + +// NewEventsPublisher creates a new instance of EventsPublisher. +// It requires a configuration instance to create the publisher. +// +// Parameters: +// - config: The configuration for the Kafka publisher. +// This configuration includes the Kafka broker addresses, TLS settings, and other options. +// +// Returns: +// - *EventsPublisher: The new instance of EventsPublisher. +// - error: An error if the publisher could not be created. +func NewEventsPublisher(config *Config) (*EventsPublisher, error) { + saramaConfig := toSaramaConfig(config) + producer, err := sarama.NewSyncProducer(config.Brokers, saramaConfig) + if err != nil { + return nil, err + } + + return &EventsPublisher{ + config: config, + started: atomic.NewBool(true), + producer: producer, + }, nil +} + +// Close implements ego.EventPublisher. +func (x *EventsPublisher) Close(ctx context.Context) error { + // we give the publisher 3 seconds to close. This is an abitrary value. + // It helps to ensure that the publisher has enough time to close. + ctx, cancel := context.WithTimeout(ctx, 3*time.Second) + defer cancel() + + x.started.Store(false) + return x.producer.Close() +} + +// ID implements ego.EventPublisher. +func (x *EventsPublisher) ID() string { + return "ego-kafka" +} + +// Publish implements ego.EventPublisher. +// It publishes an event to the Kafka broker. +// +// Parameters: +// - ctx: The context for managing cancellation and timeouts. +// - event: The event to be published. +// +// Returns: +// - error: If an error occurs during event publication, it is returned. +func (x *EventsPublisher) Publish(ctx context.Context, event *egopb.Event) error { + if !x.started.Load() { + return ego.ErrPublisherNotStarted + } + + // serialize the event. No need to check for errors. + payload, _ := proto.Marshal(event) + + // create a new producer message + message := &sarama.ProducerMessage{ + Topic: x.config.EventsTopic, + Key: sarama.StringEncoder(event.GetPersistenceId()), + Value: sarama.ByteEncoder(payload), + } + + // send the message + _, _, err := x.producer.SendMessage(message) + return err +} + +// DurableStatePublisher defines a Kafka publisher. +// This publisher is responsible for delivering DurableState to a Kafka broker. +type DurableStatePublisher struct { + config *Config + producer sarama.SyncProducer + started *atomic.Bool +} + +// ensure DurableStatesPublisher implements ego.DurableStatePublisher. +var _ ego.StatePublisher = (*DurableStatePublisher)(nil) + +// NewDurableStatePublisher creates a new instance of DurableStatePublisher. +// It requires a configuration instance to create the publisher. +// +// Parameters: +// - config: The configuration for the Kafka publisher. +// This configuration includes the Kafka broker addresses, TLS settings, and other options. +// +// Returns: +// - *DurableStatePublisher: The new instance of DurableStatePublisher. +// - error: An error if the publisher could not be created. +func NewDurableStatePublisher(config *Config) (*DurableStatePublisher, error) { + saramaConfig := toSaramaConfig(config) + producer, err := sarama.NewSyncProducer(config.Brokers, saramaConfig) + if err != nil { + return nil, err + } + + return &DurableStatePublisher{ + config: config, + started: atomic.NewBool(true), + producer: producer, + }, nil +} + +// Close implements ego.StatePublisher. +func (x *DurableStatePublisher) Close(ctx context.Context) error { + // we give the publisher 3 seconds to close. This is an abitrary value. + // It helps to ensure that the publisher has enough time to close. + ctx, cancel := context.WithTimeout(ctx, 3*time.Second) + defer cancel() + + x.started.Store(false) + return x.producer.Close() +} + +// ID implements ego.StatePublisher. +func (x *DurableStatePublisher) ID() string { + return "ego-kafka" +} + +// Publish implements ego.StatePublisher. +// It publishes an event to the Kafka broker. +// +// Parameters: +// - ctx: The context for managing cancellation and timeouts. +// - state: The durable state to be published. +// +// Returns: +// - error: If an error occurs during event publication, it is returned. +func (x *DurableStatePublisher) Publish(ctx context.Context, state *egopb.DurableState) error { + if !x.started.Load() { + return ego.ErrPublisherNotStarted + } + + // serialize the event. No need to check for errors. + payload, _ := proto.Marshal(state) + + // create a new producer message + message := &sarama.ProducerMessage{ + Topic: x.config.StateTopic, + Key: sarama.StringEncoder(state.GetPersistenceId()), + Value: sarama.ByteEncoder(payload), + } + + // send the message + _, _, err := x.producer.SendMessage(message) + return err +} diff --git a/publisher/nats/config.go b/publisher/nats/config.go new file mode 100644 index 0000000..a7dc4e4 --- /dev/null +++ b/publisher/nats/config.go @@ -0,0 +1,82 @@ +/* + * MIT License + * + * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package nats + +import ( + "fmt" + "strings" + "time" + + "github.com/tochemey/gopack/validation" +) + +// Config is a set of base config values required for connecting to NATS +type Config struct { + // NatsServer defines the nats server in the format nats://host:port + NatsServer string + // NatsSubject defines the custom NATS subject + NatsSubject string + // MaxJoinAttempts denotes the maximum number of attempts to connect an existing NATs server + MaxJoinAttempts int + // ReconnectWait sets the time to backoff after attempting a reconnect + // to a server that we were already connected to previously. + ReconnectWait time.Duration +} + +var _ validation.Validator = (*Config)(nil) + +func (x Config) Validate() error { + return validation.New(validation.FailFast()). + AddValidator(validation.NewEmptyStringValidator("NatsServer", x.NatsServer)). + AddValidator(NewServerAddrValidator(x.NatsServer)). + AddValidator(validation.NewEmptyStringValidator("NatsSubject", x.NatsSubject)). + AddAssertion(x.MaxJoinAttempts > 0, "MaxJoinAttempts must be greater than 0"). + AddAssertion(x.ReconnectWait > 0, "ReconnectWait must be greater than 0"). + Validate() +} + +// ServerAddrValidator helps validates the NATs server address +type ServerAddrValidator struct { + server string +} + +// enforce compilation error +var _ validation.Validator = (*ServerAddrValidator)(nil) + +// NewServerAddrValidator validates the nats server address +func NewServerAddrValidator(server string) validation.Validator { + return &ServerAddrValidator{server: server} +} + +// Validate execute the validation code +func (x *ServerAddrValidator) Validate() error { + // make sure that the nats prefix is set in the server address + if !strings.HasPrefix(x.server, "nats") { + return fmt.Errorf("invalid nats server address: %s", x.server) + } + + hostAndPort := strings.SplitN(x.server, "nats://", 2)[1] + return validation.NewTCPAddressValidator(hostAndPort).Validate() +} diff --git a/publisher/nats/doc.go b/publisher/nats/doc.go new file mode 100644 index 0000000..e5534fb --- /dev/null +++ b/publisher/nats/doc.go @@ -0,0 +1,26 @@ +/* + * MIT License + * + * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// Package nats provides NATS streaming connectors. +package nats diff --git a/publisher/nats/go.mod b/publisher/nats/go.mod new file mode 100644 index 0000000..3c700f8 --- /dev/null +++ b/publisher/nats/go.mod @@ -0,0 +1,66 @@ +module github.com/tochemey/ego/v3/publisher/nats + +go 1.23.0 + +require ( + github.com/nats-io/nats.go v1.39.0 + github.com/tochemey/ego/v3 v3.3.3-0.20250217013204-7bf60265a95d + github.com/tochemey/goakt/v3 v3.0.0 // indirect + github.com/tochemey/gopack v0.2.0 + go.uber.org/multierr v1.11.0 // indirect +) + +require ( + github.com/flowchartsman/retry v1.2.0 + go.uber.org/atomic v1.11.0 + google.golang.org/protobuf v1.36.5 +) + +require ( + connectrpc.com/connect v1.18.1 // indirect + github.com/RoaringBitmap/roaring v1.9.4 // indirect + github.com/Workiva/go-datastructures v1.1.5 // indirect + github.com/armon/go-metrics v0.4.1 // indirect + github.com/bits-and-blooms/bitset v1.20.0 // indirect + github.com/buraksezer/consistent v0.10.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/deckarep/golang-set/v2 v2.7.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/google/btree v1.1.3 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-metrics v0.5.4 // indirect + github.com/hashicorp/go-msgpack/v2 v2.1.2 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-sockaddr v1.0.7 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/hashicorp/logutils v1.0.0 // indirect + github.com/hashicorp/memberlist v0.5.3 // indirect + github.com/klauspost/compress v1.17.11 // indirect + github.com/klauspost/cpuid/v2 v2.2.9 // indirect + github.com/miekg/dns v1.1.63 // indirect + github.com/mschoch/smat v0.2.0 // indirect + github.com/nats-io/nkeys v0.4.9 // indirect + github.com/nats-io/nuid v1.0.1 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/redis/go-redis/v9 v9.7.0 // indirect + github.com/reugn/go-quartz v0.14.0 // indirect + github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/tidwall/btree v1.7.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/redcon v1.6.2 // indirect + github.com/tochemey/olric v0.2.0 // indirect + github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + github.com/zeebo/xxh3 v1.0.2 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/crypto v0.33.0 // indirect + golang.org/x/mod v0.23.0 // indirect + golang.org/x/net v0.35.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect + golang.org/x/tools v0.30.0 // indirect +) diff --git a/publisher/nats/go.sum b/publisher/nats/go.sum new file mode 100644 index 0000000..214a5ab --- /dev/null +++ b/publisher/nats/go.sum @@ -0,0 +1,363 @@ +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= +connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/RoaringBitmap/roaring v1.9.4 h1:yhEIoH4YezLYT04s1nHehNO64EKFTop/wBhxv2QzDdQ= +github.com/RoaringBitmap/roaring v1.9.4/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90= +github.com/Workiva/go-datastructures v1.1.5 h1:5YfhQ4ry7bZc2Mc7R0YZyYwpf5c6t1cEFvdAhd6Mkf4= +github.com/Workiva/go-datastructures v1.1.5/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= +github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU= +github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/buraksezer/consistent v0.10.0 h1:hqBgz1PvNLC5rkWcEBVAL9dFMBWz6I0VgUCW25rrZlU= +github.com/buraksezer/consistent v0.10.0/go.mod h1:6BrVajWq7wbKZlTOUPs/XVfR8c0maujuPowduSpZqmw= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set/v2 v2.7.0 h1:gIloKvD7yH2oip4VLhsv3JyLLFnC0Y2mlusgcvJYW5k= +github.com/deckarep/golang-set/v2 v2.7.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= +github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/flowchartsman/retry v1.2.0 h1:qDhlw6RNufXz6RGr+IiYimFpMMkt77SUSHY5tgFaUCU= +github.com/flowchartsman/retry v1.2.0/go.mod h1:+sfx8OgCCiAr3t5jh2Gk+T0fRTI+k52edaYxURQxY64= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-metrics v0.5.4 h1:8mmPiIJkTPPEbAiV97IxdAGNdRdaWwVap1BU6elejKY= +github.com/hashicorp/go-metrics v0.5.4/go.mod h1:CG5yz4NZ/AI/aQt9Ucm/vdBnbh7fvmv4lxZ350i+QQI= +github.com/hashicorp/go-msgpack/v2 v2.1.2 h1:4Ee8FTp834e+ewB71RDrQ0VKpyFdrKOjvYtnQ/ltVj0= +github.com/hashicorp/go-msgpack/v2 v2.1.2/go.mod h1:upybraOAblm4S7rx0+jeNy+CWWhzywQsSRV5033mMu4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw= +github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw= +github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/memberlist v0.5.3 h1:tQ1jOCypD0WvMemw/ZhhtH+PWpzcftQvgCorLu0hndk= +github.com/hashicorp/memberlist v0.5.3/go.mod h1:h60o12SZn/ua/j0B6iKAZezA4eDaGsIuPO70eOaJ6WE= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kapetan-io/tackle v0.11.0 h1:xcQ2WgES8rjsd0ZMBfFTMuCs8YG4+1r2OAPY0+mHXjM= +github.com/kapetan-io/tackle v0.11.0/go.mod h1:94m0H3j8pm9JMsAuqBsC/Y08WpAUh01ugkFxABjjHd8= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= +github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= +github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY= +github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs= +github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q= +github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= +github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nats-io/jwt/v2 v2.7.3 h1:6bNPK+FXgBeAqdj4cYQ0F8ViHRbi7woQLq4W29nUAzE= +github.com/nats-io/jwt/v2 v2.7.3/go.mod h1:GvkcbHhKquj3pkioy5put1wvPxs78UlZ7D/pY+BgZk4= +github.com/nats-io/nats-server/v2 v2.10.25 h1:J0GWLDDXo5HId7ti/lTmBfs+lzhmu8RPkoKl0eSCqwc= +github.com/nats-io/nats-server/v2 v2.10.25/go.mod h1:/YYYQO7cuoOBt+A7/8cVjuhWTaTUEAlZbJT+3sMAfFU= +github.com/nats-io/nats.go v1.39.0 h1:2/yg2JQjiYYKLwDuBzV0FbB2sIV+eFNkEevlRi4n9lI= +github.com/nats-io/nats.go v1.39.0/go.mod h1:MgRb8oOdigA6cYpEPhXJuRVH6UE/V4jblJ2jQ27IXYM= +github.com/nats-io/nkeys v0.4.9 h1:qe9Faq2Gxwi6RZnZMXfmGMZkg3afLLOtrU+gDZJ35b0= +github.com/nats-io/nkeys v0.4.9/go.mod h1:jcMqs+FLG+W5YO36OX6wFIFcmpdAns+w1Wm6D3I/evE= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= +github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= +github.com/reugn/go-quartz v0.14.0 h1:KlIBAsOIw1JI8Rc7/f8VrrHBHOr+BiqrTiB35pRe84M= +github.com/reugn/go-quartz v0.14.0/go.mod h1:00DVnBKq2Fxag/HlR9mGXjmHNlMFQ1n/LNM+Fn0jUaE= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tidwall/btree v1.1.0/go.mod h1:TzIRzen6yHbibdSfK6t8QimqbUnoxUSrZfeW7Uob0q4= +github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= +github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/redcon v1.6.2 h1:5qfvrrybgtO85jnhSravmkZyC0D+7WstbfCs3MmPhow= +github.com/tidwall/redcon v1.6.2/go.mod h1:p5Wbsgeyi2VSTBWOcA5vRXrOb9arFTcU2+ZzFjqV75Y= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/tochemey/ego/v3 v3.3.3-0.20250217013204-7bf60265a95d h1:Hef0/RiGdLpzO7ZB8CB7oi+EjTfk61M5uYos3lDSCf0= +github.com/tochemey/ego/v3 v3.3.3-0.20250217013204-7bf60265a95d/go.mod h1:TuowN3LV6YVItwS4MZlyZ51oZBd75XIylYn0XwKSETM= +github.com/tochemey/goakt/v3 v3.0.0 h1:WukWOsXbaCkIjXWKm0GHY81s6yKeHDE06Vj3O42cJ4c= +github.com/tochemey/goakt/v3 v3.0.0/go.mod h1:I3F45cKjyQiWnTqY4PLcnM9hBYhzb3vjulUoD9ComNc= +github.com/tochemey/gopack v0.2.0 h1:LpjE95dfqSQepHC/chAszP64R+w4tj5PivaTJHTFnz0= +github.com/tochemey/gopack v0.2.0/go.mod h1:3FIX2GYTolKNK4Qjh/ju0oiYHC9fVCT/H+fobcwlnQo= +github.com/tochemey/olric v0.2.0 h1:y/uoVNNVdMn+pbUI01i6TqeD48+VgjRcoRGOzVzkdVY= +github.com/tochemey/olric v0.2.0/go.mod h1:vRpB3Xb0L4J7acugeAfrIXcwLDlCwRsC10s1jCYND1Y= +github.com/travisjeffery/go-dynaport v1.0.0 h1:m/qqf5AHgB96CMMSworIPyo1i7NZueRsnwdzdCJ8Ajw= +github.com/travisjeffery/go-dynaport v1.0.0/go.mod h1:0LHuDS4QAx+mAc4ri3WkQdavgVoBIZ7cE9ob17KIAJk= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= +golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= +golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.32.2 h1:bZrMLEkgizC24G9eViHGOPbW+aRo9duEISRIJKfdJuw= +k8s.io/api v0.32.2/go.mod h1:hKlhk4x1sJyYnHENsrdCWw31FEmCijNGPJO5WzHiJ6Y= +k8s.io/apimachinery v0.32.2 h1:yoQBR9ZGkA6Rgmhbp/yuT9/g+4lxtsGYwW6dR6BDPLQ= +k8s.io/apimachinery v0.32.2/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/client-go v0.32.2 h1:4dYCD4Nz+9RApM2b/3BtVvBHw54QjMFUl1OLcJG5yOA= +k8s.io/client-go v0.32.2/go.mod h1:fpZ4oJXclZ3r2nDOv+Ux3XcJutfrwjKTCHz2H3sww94= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg= +k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas= +k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= +k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk= +sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/publisher/nats/nats.go b/publisher/nats/nats.go new file mode 100644 index 0000000..0c03c86 --- /dev/null +++ b/publisher/nats/nats.go @@ -0,0 +1,271 @@ +/* + * MIT License + * + * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package nats + +import ( + "context" + + "github.com/flowchartsman/retry" + "github.com/nats-io/nats.go" + "go.uber.org/atomic" + "google.golang.org/protobuf/proto" + + "github.com/tochemey/ego/v3" + "github.com/tochemey/ego/v3/egopb" +) + +// EventsPublisher defines a NATS publisher. +// This publisher is responsible for delivering ego events to a NATS server. +type EventsPublisher struct { + config *Config + started *atomic.Bool + connection *nats.Conn + jetStream nats.JetStream +} + +// ensure EventsPublisher implements ego.EventPublisher. +var _ ego.EventPublisher = (*EventsPublisher)(nil) + +// NewEventsPublisher creates a new instance of EventsPublisher. +// It requires a configuration instance to create the publisher. +// +// Parameters: +// - config: The configuration for the NATS publisher. +// This configuration includes the NATS server addresses, TLS settings, and other options. +// +// Returns: The new instance of EventsPublisher. +func NewEventsPublisher(config *Config) (*EventsPublisher, error) { + if err := config.Validate(); err != nil { + return nil, err + } + + // TODO: add additional configuration options + opts := nats.GetDefaultOptions() + opts.Url = config.NatsServer + opts.ReconnectWait = config.ReconnectWait + opts.MaxReconnect = -1 + + var ( + connection *nats.Conn + err error + ) + + // let us connect using an exponential backoff mechanism + // create a new instance of retrier that will try a maximum of five times, with + // an initial delay and a maximum delay of opts.ReconnectWait + retrier := retry.NewRetrier(config.MaxJoinAttempts, opts.ReconnectWait, opts.ReconnectWait) + err = retrier.Run(func() error { + connection, err = opts.Connect() + if err != nil { + return err + } + return nil + }) + + if err != nil { + return nil, err + } + + // create an instance of JetStream + // TODO: add additional configuration options + jetStream, err := connection.JetStream() + if err != nil { + return nil, err + } + + _, err = jetStream.AddStream(&nats.StreamConfig{ + Name: "ego-events", + Subjects: []string{config.NatsSubject}, + }) + if err != nil { + return nil, err + } + + return &EventsPublisher{ + config: config, + started: atomic.NewBool(true), + connection: connection, + jetStream: jetStream, + }, nil +} + +// Close closes the NATS publisher. +// It stops the publisher from sending events to the NATS server. +// +// Parameters: +// - ctx: The context to close the publisher. +// +// Returns: An error if the publisher fails to close. +func (x *EventsPublisher) Close(context.Context) error { + x.started.Store(false) + if err := x.connection.Drain(); err != nil { + return err + } + x.connection.Close() + return nil +} + +// ID returns the ID of the NATS publisher. +func (x *EventsPublisher) ID() string { + return "ego-nats" +} + +// Publish sends an event to the NATS server. +// +// Parameters: +// - ctx: The context to send the event. +// - event: The event to send. +// +// Returns: An error if the event fails to be sent. +func (x *EventsPublisher) Publish(_ context.Context, event *egopb.Event) error { + if !x.started.Load() { + return ego.ErrPublisherNotStarted + } + + // serialize the event. No need to check for errors. + payload, _ := proto.Marshal(event) + + _, err := x.jetStream.Publish(x.config.NatsSubject, payload) + if err != nil { + return err + } + return nil +} + +type DurableStatePublisher struct { + config *Config + started *atomic.Bool + connection *nats.Conn + jetStream nats.JetStream +} + +// ensure DurableStatesPublisher implements ego.DurableStatePublisher. +var _ ego.StatePublisher = (*DurableStatePublisher)(nil) + +// NewDurableStatePublisher creates a new instance of DurableStatePublisher. +// It requires a configuration instance to create the publisher. +// +// Parameters: +// - config: The configuration for the NATS publisher. +// This configuration includes the NATS server addresses, TLS settings, and other options. +// +// Returns: The new instance of DurableStatePublisher. +func NewDurableStatePublisher(config *Config) (*DurableStatePublisher, error) { + if err := config.Validate(); err != nil { + return nil, err + } + + // TODO: add additional configuration options + opts := nats.GetDefaultOptions() + opts.Url = config.NatsServer + opts.ReconnectWait = config.ReconnectWait + opts.MaxReconnect = -1 + + var ( + connection *nats.Conn + err error + ) + + // let us connect using an exponential backoff mechanism + // create a new instance of retrier that will try a maximum of five times, with + // an initial delay and a maximum delay of opts.ReconnectWait + retrier := retry.NewRetrier(config.MaxJoinAttempts, opts.ReconnectWait, opts.ReconnectWait) + err = retrier.Run(func() error { + connection, err = opts.Connect() + if err != nil { + return err + } + return nil + }) + + if err != nil { + return nil, err + } + + // create an instance of JetStream + // TODO: add additional configuration options + jetStream, err := connection.JetStream() + if err != nil { + return nil, err + } + + _, err = jetStream.AddStream(&nats.StreamConfig{ + Name: "ego-durable-states", + Subjects: []string{config.NatsSubject}, + }) + if err != nil { + return nil, err + } + + return &DurableStatePublisher{ + config: config, + started: atomic.NewBool(true), + connection: connection, + jetStream: jetStream, + }, nil +} + +// Close closes the NATS publisher. +// It stops the publisher from sending events to the NATS server. +// +// Parameters: +// - ctx: The context to close the publisher. +// +// Returns: An error if the publisher fails to close. +func (e *DurableStatePublisher) Close(context.Context) error { + e.started.Store(false) + if err := e.connection.Drain(); err != nil { + return err + } + e.connection.Close() + return nil +} + +// ID returns the ID of the NATS publisher. +func (e *DurableStatePublisher) ID() string { + return "ego-nats" +} + +// Publish sends a message to the NATS server. +// +// Parameters: +// - ctx: The context to send the event. +// - state: The durable state to send. +// +// Returns: An error if the event fails to be sent. +func (e *DurableStatePublisher) Publish(_ context.Context, state *egopb.DurableState) error { + if !e.started.Load() { + return ego.ErrPublisherNotStarted + } + + // serialize the event. No need to check for errors. + payload, _ := proto.Marshal(state) + + _, err := e.jetStream.Publish(e.config.NatsSubject, payload) + if err != nil { + return err + } + return nil +} diff --git a/publisher/pulsar/config.go b/publisher/pulsar/config.go new file mode 100644 index 0000000..1ba538d --- /dev/null +++ b/publisher/pulsar/config.go @@ -0,0 +1,79 @@ +/* + * MIT License + * + * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package pulsar + +import ( + "fmt" + "strings" + "time" + + "github.com/tochemey/gopack/validation" +) + +// Config is a set of base config values required for connecting to Pulsar +type Config struct { + // URL defines the pulsar server in the format pulsar://host:port + URL string + // EventsTopic is the topic for publishing events. + EventsTopic string + // StateTopic is the topic for publishing state changes. + StateTopic string + // ConnectionTimeout specifies the timeout for establishing a connection. + ConnectionTimeout time.Duration + // KeepAlive specifies the keep-alive period for the connection. + KeepAlive time.Duration +} + +var _ validation.Validator = (*Config)(nil) + +func (x Config) Validate() error { + return validation.New(validation.FailFast()). + AddValidator(validation.NewEmptyStringValidator("URL", x.URL)). + AddValidator(NewURLValidator(x.URL)). + AddValidator(validation.NewEmptyStringValidator("EventsTopic", x.EventsTopic)). + AddValidator(validation.NewEmptyStringValidator("StateTopic", x.StateTopic)). + Validate() +} + +type URLValidator struct { + url string +} + +// enforce compilation error +var _ validation.Validator = (*URLValidator)(nil) + +func NewURLValidator(url string) validation.Validator { + return &URLValidator{url: url} +} + +// Validate execute the validation code +func (x *URLValidator) Validate() error { + if !strings.HasPrefix(x.url, "pulsar") { + return fmt.Errorf("invalid nats server address: %s", x.url) + } + + hostAndPort := strings.SplitN(x.url, "pulsar://", 2)[1] + return validation.NewTCPAddressValidator(hostAndPort).Validate() +} diff --git a/publisher/pulsar/doc.go b/publisher/pulsar/doc.go new file mode 100644 index 0000000..23f10ef --- /dev/null +++ b/publisher/pulsar/doc.go @@ -0,0 +1,26 @@ +/* + * MIT License + * + * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// Package pulsar implements the Apache Pulsar connector. +package pulsar diff --git a/publisher/pulsar/go.mod b/publisher/pulsar/go.mod new file mode 100644 index 0000000..59011b5 --- /dev/null +++ b/publisher/pulsar/go.mod @@ -0,0 +1,88 @@ +module github.com/tochemey/ego/v3/publisher/pulsar + +go 1.23.0 + +require ( + github.com/apache/pulsar-client-go v0.14.0 + github.com/tochemey/ego/v3 v3.3.3-0.20250217013204-7bf60265a95d + github.com/tochemey/gopack v0.2.0 + go.uber.org/atomic v1.11.0 + google.golang.org/protobuf v1.36.5 +) + +require ( + connectrpc.com/connect v1.18.1 // indirect + github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect + github.com/99designs/keyring v1.2.1 // indirect + github.com/AthenZ/athenz v1.10.39 // indirect + github.com/DataDog/zstd v1.5.2 // indirect + github.com/RoaringBitmap/roaring v1.9.4 // indirect + github.com/Workiva/go-datastructures v1.1.5 // indirect + github.com/ardielle/ardielle-go v1.5.2 // indirect + github.com/armon/go-metrics v0.4.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bits-and-blooms/bitset v1.20.0 // indirect + github.com/buraksezer/consistent v0.10.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/danieljoos/wincred v1.1.2 // indirect + github.com/deckarep/golang-set/v2 v2.7.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/dvsekhvalnov/jose2go v1.6.0 // indirect + github.com/flowchartsman/retry v1.2.0 // indirect + github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/btree v1.1.3 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect + github.com/hamba/avro/v2 v2.22.2-0.20240625062549-66aad10411d9 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-metrics v0.5.4 // indirect + github.com/hashicorp/go-msgpack/v2 v2.1.2 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-sockaddr v1.0.7 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/hashicorp/logutils v1.0.0 // indirect + github.com/hashicorp/memberlist v0.5.3 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.17.11 // indirect + github.com/klauspost/cpuid/v2 v2.2.9 // indirect + github.com/miekg/dns v1.1.63 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mschoch/smat v0.2.0 // indirect + github.com/mtibben/percent v0.2.1 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pierrec/lz4 v2.0.5+incompatible // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_golang v1.20.5 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.62.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/redis/go-redis/v9 v9.7.0 // indirect + github.com/reugn/go-quartz v0.14.0 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect + github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/tidwall/btree v1.7.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/redcon v1.6.2 // indirect + github.com/tochemey/goakt/v3 v3.0.0 // indirect + github.com/tochemey/olric v0.2.0 // indirect + github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + github.com/zeebo/xxh3 v1.0.2 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/mod v0.23.0 // indirect + golang.org/x/net v0.35.0 // indirect + golang.org/x/oauth2 v0.26.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/term v0.29.0 // indirect + golang.org/x/text v0.22.0 // indirect + golang.org/x/tools v0.29.0 // indirect +) diff --git a/publisher/pulsar/go.sum b/publisher/pulsar/go.sum new file mode 100644 index 0000000..6b1d60b --- /dev/null +++ b/publisher/pulsar/go.sum @@ -0,0 +1,534 @@ +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= +connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= +github.com/99designs/keyring v1.2.1 h1:tYLp1ULvO7i3fI5vE21ReQuj99QFSs7lGm0xWyJo87o= +github.com/99designs/keyring v1.2.1/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= +github.com/AthenZ/athenz v1.10.39 h1:mtwHTF/v62ewY2Z5KWhuZgVXftBej1/Tn80zx4DcawY= +github.com/AthenZ/athenz v1.10.39/go.mod h1:3Tg8HLsiQZp81BJY58JBeU2BR6B/H4/0MQGfCwhHNEA= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= +github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/RoaringBitmap/roaring v1.9.4 h1:yhEIoH4YezLYT04s1nHehNO64EKFTop/wBhxv2QzDdQ= +github.com/RoaringBitmap/roaring v1.9.4/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90= +github.com/Workiva/go-datastructures v1.1.5 h1:5YfhQ4ry7bZc2Mc7R0YZyYwpf5c6t1cEFvdAhd6Mkf4= +github.com/Workiva/go-datastructures v1.1.5/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/apache/pulsar-client-go v0.14.0 h1:P7yfAQhQ52OCAu8yVmtdbNQ81vV8bF54S2MLmCPJC9w= +github.com/apache/pulsar-client-go v0.14.0/go.mod h1:PNUE29x9G1EHMvm41Bs2vcqwgv7N8AEjeej+nEVYbX8= +github.com/ardielle/ardielle-go v1.5.2 h1:TilHTpHIQJ27R1Tl/iITBzMwiUGSlVfiVhwDNGM3Zj4= +github.com/ardielle/ardielle-go v1.5.2/go.mod h1:I4hy1n795cUhaVt/ojz83SNVCYIGsAFAONtv2Dr7HUI= +github.com/ardielle/ardielle-tools v1.5.4/go.mod h1:oZN+JRMnqGiIhrzkRN9l26Cej9dEx4jeNG6A+AdkShk= +github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= +github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/aws/aws-sdk-go v1.32.6/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU= +github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/buraksezer/consistent v0.10.0 h1:hqBgz1PvNLC5rkWcEBVAL9dFMBWz6I0VgUCW25rrZlU= +github.com/buraksezer/consistent v0.10.0/go.mod h1:6BrVajWq7wbKZlTOUPs/XVfR8c0maujuPowduSpZqmw= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/containerd/containerd v1.7.18 h1:jqjZTQNfXGoEaZdW1WwPU0RqSn1Bm2Ay/KJPUuO8nao= +github.com/containerd/containerd v1.7.18/go.mod h1:IYEk9/IO6wAPUz2bCMVUbsfXjzw5UNP5fLz4PsUygQ4= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= +github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= +github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set/v2 v2.7.0 h1:gIloKvD7yH2oip4VLhsv3JyLLFnC0Y2mlusgcvJYW5k= +github.com/deckarep/golang-set/v2 v2.7.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/dimfeld/httptreemux v5.0.1+incompatible h1:Qj3gVcDNoOthBAqftuD596rm4wg/adLLz5xh5CmpiCA= +github.com/dimfeld/httptreemux v5.0.1+incompatible/go.mod h1:rbUlSV+CCpv/SuqUTP/8Bk2O3LyUV436/yaRGkhP6Z0= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= +github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dvsekhvalnov/jose2go v1.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA2EjTY= +github.com/dvsekhvalnov/jose2go v1.6.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= +github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= +github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/flowchartsman/retry v1.2.0 h1:qDhlw6RNufXz6RGr+IiYimFpMMkt77SUSHY5tgFaUCU= +github.com/flowchartsman/retry v1.2.0/go.mod h1:+sfx8OgCCiAr3t5jh2Gk+T0fRTI+k52edaYxURQxY64= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= +github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= +github.com/hamba/avro/v2 v2.22.2-0.20240625062549-66aad10411d9 h1:NEoabXt33PDWK4fXryK4e+XX+fSKDmmu9vg3yb9YI2M= +github.com/hamba/avro/v2 v2.22.2-0.20240625062549-66aad10411d9/go.mod h1:fQVdB2mFZBhPW1D5Abej41LMvrErARGrrdjOnKbm5yw= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-metrics v0.5.4 h1:8mmPiIJkTPPEbAiV97IxdAGNdRdaWwVap1BU6elejKY= +github.com/hashicorp/go-metrics v0.5.4/go.mod h1:CG5yz4NZ/AI/aQt9Ucm/vdBnbh7fvmv4lxZ350i+QQI= +github.com/hashicorp/go-msgpack/v2 v2.1.2 h1:4Ee8FTp834e+ewB71RDrQ0VKpyFdrKOjvYtnQ/ltVj0= +github.com/hashicorp/go-msgpack/v2 v2.1.2/go.mod h1:upybraOAblm4S7rx0+jeNy+CWWhzywQsSRV5033mMu4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw= +github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw= +github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/memberlist v0.5.3 h1:tQ1jOCypD0WvMemw/ZhhtH+PWpzcftQvgCorLu0hndk= +github.com/hashicorp/memberlist v0.5.3/go.mod h1:h60o12SZn/ua/j0B6iKAZezA4eDaGsIuPO70eOaJ6WE= +github.com/jawher/mow.cli v1.0.4/go.mod h1:5hQj2V8g+qYmLUVWqu4Wuja1pI57M83EChYLVZ0sMKk= +github.com/jawher/mow.cli v1.2.0/go.mod h1:y+pcA3jBAdo/GIZx/0rFjw/K2bVEODP9rfZOfaiq8Ko= +github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kapetan-io/tackle v0.11.0 h1:xcQ2WgES8rjsd0ZMBfFTMuCs8YG4+1r2OAPY0+mHXjM= +github.com/kapetan-io/tackle v0.11.0/go.mod h1:94m0H3j8pm9JMsAuqBsC/Y08WpAUh01ugkFxABjjHd8= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= +github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= +github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY= +github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs= +github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q= +github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo= +github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= +github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= +github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= +github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nats-io/jwt/v2 v2.7.3 h1:6bNPK+FXgBeAqdj4cYQ0F8ViHRbi7woQLq4W29nUAzE= +github.com/nats-io/jwt/v2 v2.7.3/go.mod h1:GvkcbHhKquj3pkioy5put1wvPxs78UlZ7D/pY+BgZk4= +github.com/nats-io/nats-server/v2 v2.10.25 h1:J0GWLDDXo5HId7ti/lTmBfs+lzhmu8RPkoKl0eSCqwc= +github.com/nats-io/nats-server/v2 v2.10.25/go.mod h1:/YYYQO7cuoOBt+A7/8cVjuhWTaTUEAlZbJT+3sMAfFU= +github.com/nats-io/nats.go v1.39.0 h1:2/yg2JQjiYYKLwDuBzV0FbB2sIV+eFNkEevlRi4n9lI= +github.com/nats-io/nats.go v1.39.0/go.mod h1:MgRb8oOdigA6cYpEPhXJuRVH6UE/V4jblJ2jQ27IXYM= +github.com/nats-io/nkeys v0.4.9 h1:qe9Faq2Gxwi6RZnZMXfmGMZkg3afLLOtrU+gDZJ35b0= +github.com/nats-io/nkeys v0.4.9/go.mod h1:jcMqs+FLG+W5YO36OX6wFIFcmpdAns+w1Wm6D3I/evE= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= +github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= +github.com/reugn/go-quartz v0.14.0 h1:KlIBAsOIw1JI8Rc7/f8VrrHBHOr+BiqrTiB35pRe84M= +github.com/reugn/go-quartz v0.14.0/go.mod h1:00DVnBKq2Fxag/HlR9mGXjmHNlMFQ1n/LNM+Fn0jUaE= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= +github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo= +github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4= +github.com/tidwall/btree v1.1.0/go.mod h1:TzIRzen6yHbibdSfK6t8QimqbUnoxUSrZfeW7Uob0q4= +github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= +github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/redcon v1.6.2 h1:5qfvrrybgtO85jnhSravmkZyC0D+7WstbfCs3MmPhow= +github.com/tidwall/redcon v1.6.2/go.mod h1:p5Wbsgeyi2VSTBWOcA5vRXrOb9arFTcU2+ZzFjqV75Y= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/tochemey/ego/v3 v3.3.3-0.20250217013204-7bf60265a95d h1:Hef0/RiGdLpzO7ZB8CB7oi+EjTfk61M5uYos3lDSCf0= +github.com/tochemey/ego/v3 v3.3.3-0.20250217013204-7bf60265a95d/go.mod h1:TuowN3LV6YVItwS4MZlyZ51oZBd75XIylYn0XwKSETM= +github.com/tochemey/goakt/v3 v3.0.0 h1:WukWOsXbaCkIjXWKm0GHY81s6yKeHDE06Vj3O42cJ4c= +github.com/tochemey/goakt/v3 v3.0.0/go.mod h1:I3F45cKjyQiWnTqY4PLcnM9hBYhzb3vjulUoD9ComNc= +github.com/tochemey/gopack v0.2.0 h1:LpjE95dfqSQepHC/chAszP64R+w4tj5PivaTJHTFnz0= +github.com/tochemey/gopack v0.2.0/go.mod h1:3FIX2GYTolKNK4Qjh/ju0oiYHC9fVCT/H+fobcwlnQo= +github.com/tochemey/olric v0.2.0 h1:y/uoVNNVdMn+pbUI01i6TqeD48+VgjRcoRGOzVzkdVY= +github.com/tochemey/olric v0.2.0/go.mod h1:vRpB3Xb0L4J7acugeAfrIXcwLDlCwRsC10s1jCYND1Y= +github.com/travisjeffery/go-dynaport v1.0.0 h1:m/qqf5AHgB96CMMSworIPyo1i7NZueRsnwdzdCJ8Ajw= +github.com/travisjeffery/go-dynaport v1.0.0/go.mod h1:0LHuDS4QAx+mAc4ri3WkQdavgVoBIZ7cE9ob17KIAJk= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib v1.34.0 h1:3M0wJFV+OsN1a8FRgQ14VtE1K79m+LvuykJMYSpM3Oo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= +golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= +golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= +golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.32.2 h1:bZrMLEkgizC24G9eViHGOPbW+aRo9duEISRIJKfdJuw= +k8s.io/api v0.32.2/go.mod h1:hKlhk4x1sJyYnHENsrdCWw31FEmCijNGPJO5WzHiJ6Y= +k8s.io/apimachinery v0.32.2 h1:yoQBR9ZGkA6Rgmhbp/yuT9/g+4lxtsGYwW6dR6BDPLQ= +k8s.io/apimachinery v0.32.2/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/client-go v0.32.2 h1:4dYCD4Nz+9RApM2b/3BtVvBHw54QjMFUl1OLcJG5yOA= +k8s.io/client-go v0.32.2/go.mod h1:fpZ4oJXclZ3r2nDOv+Ux3XcJutfrwjKTCHz2H3sww94= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg= +k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas= +k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= +k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk= +sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/publisher/pulsar/pulsar.go b/publisher/pulsar/pulsar.go new file mode 100644 index 0000000..e905013 --- /dev/null +++ b/publisher/pulsar/pulsar.go @@ -0,0 +1,245 @@ +/* + * MIT License + * + * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package pulsar + +import ( + "context" + "fmt" + "time" + + "github.com/apache/pulsar-client-go/pulsar" + "go.uber.org/atomic" + "google.golang.org/protobuf/proto" + + "github.com/tochemey/ego/v3" + "github.com/tochemey/ego/v3/egopb" +) + +// EventsPublisher defines a Pulsar publisher. +// This publisher is responsible for delivering ego events to a Pulsar server. +type EventsPublisher struct { + config *Config + client pulsar.Client + producer pulsar.Producer + started *atomic.Bool +} + +// ensure EventsPublisher implements ego.EventPublisher. +var _ ego.EventPublisher = (*EventsPublisher)(nil) + +// NewEventsPublisher creates a new instance of EventsPublisher. +// +// Parameters: +// - config: The configuration to use. +// +// Returns: The new instance of EventsPublisher or an error if the publisher cannot be created. +func NewEventsPublisher(config *Config) (*EventsPublisher, error) { + if err := config.Validate(); err != nil { + return nil, fmt.Errorf("invalid configuration: %w", err) + } + + // set the connection timeout and default it to 5 seconds + connectionTimeout := config.ConnectionTimeout + if connectionTimeout == 0 { + connectionTimeout = 5 * time.Second + } + + // set the keep alive and default it to 30 seconds + keepAlive := config.KeepAlive + if keepAlive == 0 { + keepAlive = 30 * time.Second + } + + // create a new Pulsar client + client, err := pulsar.NewClient(pulsar.ClientOptions{ + URL: config.URL, + ConnectionTimeout: connectionTimeout, + KeepAliveInterval: keepAlive, + }) + + if err != nil { + return nil, fmt.Errorf("failed to create client: %w", err) + } + + // create a new Pulsar producer + // TODO: add more producer options + producer, err := client.CreateProducer(pulsar.ProducerOptions{ + Topic: config.EventsTopic, + }) + + if err != nil { + return nil, fmt.Errorf("failed to create producer: %w", err) + } + + return &EventsPublisher{ + client: client, + producer: producer, + started: atomic.NewBool(true), + }, nil +} + +// ID returns the publisher ID. +func (x *EventsPublisher) ID() string { + return "ego-pulsar" +} + +// Publish publishes an event to the Pulsar server. +// +// Parameters: +// - ctx: The context to use for publishing the event. +// - event: The event to publish. +// +// Returns: An error if the event cannot be published. +func (x *EventsPublisher) Publish(ctx context.Context, event *egopb.Event) error { + if !x.started.Load() { + return ego.ErrPublisherNotStarted + } + + // serialize the event. No need to check for errors. + payload, _ := proto.Marshal(event) + _, err := x.producer.Send(ctx, &pulsar.ProducerMessage{ + Key: event.GetPersistenceId(), + OrderingKey: event.GetPersistenceId(), + Payload: payload, + }) + return err +} + +// Close closes the publisher. +// It releases all resources associated with the publisher. +// +// Parameters: +// - ctx: The context to use for closing the publisher. +// +// Returns: An error if the publisher cannot be closed. +func (x *EventsPublisher) Close(context.Context) error { + x.started.Store(false) + x.producer.Close() + x.client.Close() + return nil +} + +// EventsPublisher defines a Pulsar publisher. +// This publisher is responsible for delivering ego events to a Pulsar server. +type DurableStatePublisher struct { + client pulsar.Client + producer pulsar.Producer + started *atomic.Bool +} + +// ensure DurableStatePublisher implements ego.StatePublisher. +var _ ego.StatePublisher = (*DurableStatePublisher)(nil) + +// NewDurableStatePublisher creates a new instance of DurableStatePublisher. +// +// Parameters: +// - config: The configuration to use. +// +// Returns: The new instance of EventsPublisher or an error if the publisher cannot be created. +func NewDurableStatePublisher(config *Config) (*DurableStatePublisher, error) { + if err := config.Validate(); err != nil { + return nil, fmt.Errorf("invalid configuration: %w", err) + } + + // set the connection timeout and default it to 5 seconds + connectionTimeout := config.ConnectionTimeout + if connectionTimeout == 0 { + connectionTimeout = 5 * time.Second + } + + // set the keep alive and default it to 30 seconds + keepAlive := config.KeepAlive + if keepAlive == 0 { + keepAlive = 30 * time.Second + } + + // create a new Pulsar client + client, err := pulsar.NewClient(pulsar.ClientOptions{ + URL: config.URL, + ConnectionTimeout: connectionTimeout, + KeepAliveInterval: keepAlive, + }) + + if err != nil { + return nil, fmt.Errorf("failed to create client: %w", err) + } + + // create a new Pulsar producer + // TODO: add more producer options + producer, err := client.CreateProducer(pulsar.ProducerOptions{ + Topic: config.StateTopic, + }) + + if err != nil { + return nil, fmt.Errorf("failed to create producer: %w", err) + } + + return &DurableStatePublisher{ + client: client, + producer: producer, + started: atomic.NewBool(true), + }, nil +} + +// ID returns the publisher ID. +func (x *DurableStatePublisher) ID() string { + return "ego-pulsar" +} + +// Publish publishes an event to the Pulsar server. +// +// Parameters: +// - ctx: The context to use for publishing the event. +// - event: The event to publish. +// +// Returns: An error if the event cannot be published. +func (x *DurableStatePublisher) Publish(ctx context.Context, state *egopb.DurableState) error { + if !x.started.Load() { + return ego.ErrPublisherNotStarted + } + + // serialize the event. No need to check for errors. + payload, _ := proto.Marshal(state) + _, err := x.producer.Send(ctx, &pulsar.ProducerMessage{ + Key: state.GetPersistenceId(), + OrderingKey: state.GetPersistenceId(), + Payload: payload, + }) + return err +} + +// Close closes the publisher. +// It releases all resources associated with the publisher. +// +// Parameters: +// - ctx: The context to use for closing the publisher. +// +// Returns: An error if the publisher cannot be closed. +func (x *DurableStatePublisher) Close(context.Context) error { + x.started.Store(false) + x.producer.Close() + x.client.Close() + return nil +} diff --git a/publisher/websocket/config.go b/publisher/websocket/config.go new file mode 100644 index 0000000..96b3f51 --- /dev/null +++ b/publisher/websocket/config.go @@ -0,0 +1,43 @@ +/* + * MIT License + * + * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package websocket + +import ( + "github.com/tochemey/gopack/validation" +) + +// Config is a set of base config values required for connecting to a websocket server +type Config struct { + // URL defines the websocket server in the format ws://host:port/path or wss://host:port/path + URL string +} + +var _ validation.Validator = (*Config)(nil) + +func (x Config) Validate() error { + return validation.New(validation.FailFast()). + AddValidator(validation.NewEmptyStringValidator("URL", x.URL)). + Validate() +} diff --git a/publisher/websocket/doc.go b/publisher/websocket/doc.go new file mode 100644 index 0000000..7b960fc --- /dev/null +++ b/publisher/websocket/doc.go @@ -0,0 +1,26 @@ +/* + * MIT License + * + * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// Package websocket implements the WebSocket connector. +package websocket diff --git a/publisher/websocket/go.mod b/publisher/websocket/go.mod new file mode 100644 index 0000000..ca7fc35 --- /dev/null +++ b/publisher/websocket/go.mod @@ -0,0 +1,60 @@ +module github.com/tochemey/ego/v3/publisher/websocket + +go 1.23.0 + +require ( + github.com/gorilla/websocket v1.5.3 + github.com/tochemey/ego/v3 v3.3.3-0.20250217013204-7bf60265a95d + github.com/tochemey/gopack v0.2.0 + go.uber.org/atomic v1.11.0 + google.golang.org/protobuf v1.36.5 +) + +require ( + connectrpc.com/connect v1.18.1 // indirect + github.com/RoaringBitmap/roaring v1.9.4 // indirect + github.com/Workiva/go-datastructures v1.1.5 // indirect + github.com/armon/go-metrics v0.4.1 // indirect + github.com/bits-and-blooms/bitset v1.20.0 // indirect + github.com/buraksezer/consistent v0.10.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/deckarep/golang-set/v2 v2.7.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/flowchartsman/retry v1.2.0 // indirect + github.com/google/btree v1.1.3 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-metrics v0.5.4 // indirect + github.com/hashicorp/go-msgpack/v2 v2.1.2 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-sockaddr v1.0.7 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/hashicorp/logutils v1.0.0 // indirect + github.com/hashicorp/memberlist v0.5.3 // indirect + github.com/klauspost/compress v1.17.11 // indirect + github.com/klauspost/cpuid/v2 v2.2.9 // indirect + github.com/miekg/dns v1.1.63 // indirect + github.com/mschoch/smat v0.2.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/redis/go-redis/v9 v9.7.0 // indirect + github.com/reugn/go-quartz v0.14.0 // indirect + github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/tidwall/btree v1.7.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/redcon v1.6.2 // indirect + github.com/tochemey/goakt/v3 v3.0.0 // indirect + github.com/tochemey/olric v0.2.0 // indirect + github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + github.com/zeebo/xxh3 v1.0.2 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/mod v0.23.0 // indirect + golang.org/x/net v0.35.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect + golang.org/x/tools v0.29.0 // indirect +) diff --git a/publisher/websocket/go.sum b/publisher/websocket/go.sum new file mode 100644 index 0000000..d8b9ce3 --- /dev/null +++ b/publisher/websocket/go.sum @@ -0,0 +1,365 @@ +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= +connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/RoaringBitmap/roaring v1.9.4 h1:yhEIoH4YezLYT04s1nHehNO64EKFTop/wBhxv2QzDdQ= +github.com/RoaringBitmap/roaring v1.9.4/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90= +github.com/Workiva/go-datastructures v1.1.5 h1:5YfhQ4ry7bZc2Mc7R0YZyYwpf5c6t1cEFvdAhd6Mkf4= +github.com/Workiva/go-datastructures v1.1.5/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= +github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU= +github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/buraksezer/consistent v0.10.0 h1:hqBgz1PvNLC5rkWcEBVAL9dFMBWz6I0VgUCW25rrZlU= +github.com/buraksezer/consistent v0.10.0/go.mod h1:6BrVajWq7wbKZlTOUPs/XVfR8c0maujuPowduSpZqmw= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set/v2 v2.7.0 h1:gIloKvD7yH2oip4VLhsv3JyLLFnC0Y2mlusgcvJYW5k= +github.com/deckarep/golang-set/v2 v2.7.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= +github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/flowchartsman/retry v1.2.0 h1:qDhlw6RNufXz6RGr+IiYimFpMMkt77SUSHY5tgFaUCU= +github.com/flowchartsman/retry v1.2.0/go.mod h1:+sfx8OgCCiAr3t5jh2Gk+T0fRTI+k52edaYxURQxY64= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-metrics v0.5.4 h1:8mmPiIJkTPPEbAiV97IxdAGNdRdaWwVap1BU6elejKY= +github.com/hashicorp/go-metrics v0.5.4/go.mod h1:CG5yz4NZ/AI/aQt9Ucm/vdBnbh7fvmv4lxZ350i+QQI= +github.com/hashicorp/go-msgpack/v2 v2.1.2 h1:4Ee8FTp834e+ewB71RDrQ0VKpyFdrKOjvYtnQ/ltVj0= +github.com/hashicorp/go-msgpack/v2 v2.1.2/go.mod h1:upybraOAblm4S7rx0+jeNy+CWWhzywQsSRV5033mMu4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw= +github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw= +github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/memberlist v0.5.3 h1:tQ1jOCypD0WvMemw/ZhhtH+PWpzcftQvgCorLu0hndk= +github.com/hashicorp/memberlist v0.5.3/go.mod h1:h60o12SZn/ua/j0B6iKAZezA4eDaGsIuPO70eOaJ6WE= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kapetan-io/tackle v0.11.0 h1:xcQ2WgES8rjsd0ZMBfFTMuCs8YG4+1r2OAPY0+mHXjM= +github.com/kapetan-io/tackle v0.11.0/go.mod h1:94m0H3j8pm9JMsAuqBsC/Y08WpAUh01ugkFxABjjHd8= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= +github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= +github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY= +github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs= +github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q= +github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= +github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nats-io/jwt/v2 v2.7.3 h1:6bNPK+FXgBeAqdj4cYQ0F8ViHRbi7woQLq4W29nUAzE= +github.com/nats-io/jwt/v2 v2.7.3/go.mod h1:GvkcbHhKquj3pkioy5put1wvPxs78UlZ7D/pY+BgZk4= +github.com/nats-io/nats-server/v2 v2.10.25 h1:J0GWLDDXo5HId7ti/lTmBfs+lzhmu8RPkoKl0eSCqwc= +github.com/nats-io/nats-server/v2 v2.10.25/go.mod h1:/YYYQO7cuoOBt+A7/8cVjuhWTaTUEAlZbJT+3sMAfFU= +github.com/nats-io/nats.go v1.39.0 h1:2/yg2JQjiYYKLwDuBzV0FbB2sIV+eFNkEevlRi4n9lI= +github.com/nats-io/nats.go v1.39.0/go.mod h1:MgRb8oOdigA6cYpEPhXJuRVH6UE/V4jblJ2jQ27IXYM= +github.com/nats-io/nkeys v0.4.9 h1:qe9Faq2Gxwi6RZnZMXfmGMZkg3afLLOtrU+gDZJ35b0= +github.com/nats-io/nkeys v0.4.9/go.mod h1:jcMqs+FLG+W5YO36OX6wFIFcmpdAns+w1Wm6D3I/evE= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= +github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= +github.com/reugn/go-quartz v0.14.0 h1:KlIBAsOIw1JI8Rc7/f8VrrHBHOr+BiqrTiB35pRe84M= +github.com/reugn/go-quartz v0.14.0/go.mod h1:00DVnBKq2Fxag/HlR9mGXjmHNlMFQ1n/LNM+Fn0jUaE= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tidwall/btree v1.1.0/go.mod h1:TzIRzen6yHbibdSfK6t8QimqbUnoxUSrZfeW7Uob0q4= +github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= +github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/redcon v1.6.2 h1:5qfvrrybgtO85jnhSravmkZyC0D+7WstbfCs3MmPhow= +github.com/tidwall/redcon v1.6.2/go.mod h1:p5Wbsgeyi2VSTBWOcA5vRXrOb9arFTcU2+ZzFjqV75Y= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/tochemey/ego/v3 v3.3.3-0.20250217013204-7bf60265a95d h1:Hef0/RiGdLpzO7ZB8CB7oi+EjTfk61M5uYos3lDSCf0= +github.com/tochemey/ego/v3 v3.3.3-0.20250217013204-7bf60265a95d/go.mod h1:TuowN3LV6YVItwS4MZlyZ51oZBd75XIylYn0XwKSETM= +github.com/tochemey/goakt/v3 v3.0.0 h1:WukWOsXbaCkIjXWKm0GHY81s6yKeHDE06Vj3O42cJ4c= +github.com/tochemey/goakt/v3 v3.0.0/go.mod h1:I3F45cKjyQiWnTqY4PLcnM9hBYhzb3vjulUoD9ComNc= +github.com/tochemey/gopack v0.2.0 h1:LpjE95dfqSQepHC/chAszP64R+w4tj5PivaTJHTFnz0= +github.com/tochemey/gopack v0.2.0/go.mod h1:3FIX2GYTolKNK4Qjh/ju0oiYHC9fVCT/H+fobcwlnQo= +github.com/tochemey/olric v0.2.0 h1:y/uoVNNVdMn+pbUI01i6TqeD48+VgjRcoRGOzVzkdVY= +github.com/tochemey/olric v0.2.0/go.mod h1:vRpB3Xb0L4J7acugeAfrIXcwLDlCwRsC10s1jCYND1Y= +github.com/travisjeffery/go-dynaport v1.0.0 h1:m/qqf5AHgB96CMMSworIPyo1i7NZueRsnwdzdCJ8Ajw= +github.com/travisjeffery/go-dynaport v1.0.0/go.mod h1:0LHuDS4QAx+mAc4ri3WkQdavgVoBIZ7cE9ob17KIAJk= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= +golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= +golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= +golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.32.2 h1:bZrMLEkgizC24G9eViHGOPbW+aRo9duEISRIJKfdJuw= +k8s.io/api v0.32.2/go.mod h1:hKlhk4x1sJyYnHENsrdCWw31FEmCijNGPJO5WzHiJ6Y= +k8s.io/apimachinery v0.32.2 h1:yoQBR9ZGkA6Rgmhbp/yuT9/g+4lxtsGYwW6dR6BDPLQ= +k8s.io/apimachinery v0.32.2/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/client-go v0.32.2 h1:4dYCD4Nz+9RApM2b/3BtVvBHw54QjMFUl1OLcJG5yOA= +k8s.io/client-go v0.32.2/go.mod h1:fpZ4oJXclZ3r2nDOv+Ux3XcJutfrwjKTCHz2H3sww94= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg= +k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas= +k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= +k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk= +sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/publisher/websocket/websocket.go b/publisher/websocket/websocket.go new file mode 100644 index 0000000..a9a7489 --- /dev/null +++ b/publisher/websocket/websocket.go @@ -0,0 +1,167 @@ +/* + * MIT License + * + * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package websocket + +import ( + "context" + "fmt" + + ws "github.com/gorilla/websocket" + "go.uber.org/atomic" + "google.golang.org/protobuf/proto" + + "github.com/tochemey/ego/v3" + "github.com/tochemey/ego/v3/egopb" +) + +type EventsPublisher struct { + config *Config + connection *ws.Conn + started *atomic.Bool +} + +// ensure EventsPublisher implements ego.EventPublisher. +var _ ego.EventPublisher = (*EventsPublisher)(nil) + +// NewEventsPublisher creates a new instance of EventsPublisher. +// It requires a configuration instance to create the publisher. +// +// Parameters: +// - config: The configuration for the websocket publisher. +// This configuration includes the websocket server address. +// +// Returns: The new instance of EventsPublisher. +func NewEventsPublisher(config *Config) (*EventsPublisher, error) { + if err := config.Validate(); err != nil { + return nil, fmt.Errorf("invalid configuration: %w", err) + } + + // TODO: add support for custom dialer + dialer := ws.DefaultDialer + conn, _, err := dialer.Dial(config.URL, nil) + if err != nil { + return nil, fmt.Errorf("failed to connect to the websocket server: %w", err) + } + + return &EventsPublisher{ + config: config, + connection: conn, + started: atomic.NewBool(true), + }, nil +} + +// ID returns the ID of the publisher. +func (x *EventsPublisher) ID() string { + return "ego-websocket" +} + +// Close implements ego.EventPublisher. +func (x *EventsPublisher) Close(context.Context) error { + x.started.Store(false) + return x.connection.Close() +} + +// Publish publishes an event to the websocket server. +// The event is serialized to byte array before being sent to the server. +// +// Parameters: +// - ctx: The context of the operation. +// - event: The event to publish. +// +// Returns: An error if the event cannot be published. +func (x *EventsPublisher) Publish(ctx context.Context, event *egopb.Event) error { + if !x.started.Load() { + return ego.ErrPublisherNotStarted + } + + // serialize the event. No need to check for errors. + payload, _ := proto.Marshal(event) + return x.connection.WriteMessage(ws.BinaryMessage, payload) +} + +type DurableStatePublisher struct { + config *Config + connection *ws.Conn + started *atomic.Bool +} + +// enforce compilation error +var _ ego.StatePublisher = (*DurableStatePublisher)(nil) + +// NewDurableStatePublisher creates a new instance of DurableStatePublisher. +// It requires a configuration instance to create the publisher. +// +// Parameters: +// - config: The configuration for the websocket publisher. +// This configuration includes the websocket server address. +// +// Returns: The new instance of EventsPublisher. +func NewDurableStatePublisher(config *Config) (*DurableStatePublisher, error) { + if err := config.Validate(); err != nil { + return nil, fmt.Errorf("invalid configuration: %w", err) + } + + // TODO: add support for custom dialer + dialer := ws.DefaultDialer + conn, _, err := dialer.Dial(config.URL, nil) + if err != nil { + return nil, fmt.Errorf("failed to connect to the websocket server: %w", err) + } + + return &DurableStatePublisher{ + config: config, + connection: conn, + started: atomic.NewBool(true), + }, nil +} + +// ID returns the ID of the publisher. +func (x *DurableStatePublisher) ID() string { + return "ego-websocket" +} + +// Close implements ego.EventPublisher. +func (x *DurableStatePublisher) Close(context.Context) error { + x.started.Store(false) + return x.connection.Close() +} + +// Publish publishes a durable state to the websocket server. +// The durable state is serialized to byte array before being sent to the server. +// +// Parameters: +// - ctx: The context of the operation. +// - state: The durable state to publish. +// +// Returns: An error if the state cannot be published. +func (x *DurableStatePublisher) Publish(_ context.Context, state *egopb.DurableState) error { + if !x.started.Load() { + return ego.ErrPublisherNotStarted + } + + // serialize the event. No need to check for errors. + payload, _ := proto.Marshal(state) + return x.connection.WriteMessage(ws.BinaryMessage, payload) +} diff --git a/readme.md b/readme.md index 2daa629..167d051 100644 --- a/readme.md +++ b/readme.md @@ -14,10 +14,10 @@ reliable persistence. ## Table of Content -- [Installation](#installation) -- [Versioning](#versioning) -- [Binaries and Go Versions](#binaries-and-minimum-go-versions) -- [Features](#features) +- [Installation](#-installation) +- [Versioning](#-versioning) +- [Binaries and Go Versions](#-binaries-and-minimum-go-versions) +- [Features](#-features) - [Event Sourced Behavior](#event-sourced-behavior) - [Howto](#howto) - [Events Stream](#events-stream) @@ -28,20 +28,21 @@ reliable persistence. - [State Store](#state-store) - [Howto](#howto-1) - [State Stream](#events-stream-1) -- [Cluster](#cluster) -- [Testkit](#testkit) -- [Mocks](#mocks) -- [Examples](#examples) -- [Sample](#sample) -- [Contribution](#contribution) + - [Publisher APIs](#publishers) +- [Cluster](#-cluster) +- [Testkit](#-testkit) +- [Mocks](#-mocks) +- [Examples](#-examples) +- [Sample](#-sample) +- [Contribution](#-contribution) -## Installation +## ๐Ÿ’ป Installation ```bash -go get github.com/tochemey/ego +go get github.com/tochemey/ego/v3 ``` -## Versioning +## ๐Ÿ”ข Versioning The version system adopted in eGo deviates a bit from the standard semantic versioning system. The version format is as follows: @@ -51,17 +52,18 @@ The version format is as follows: changes. - The `PATCH` part of the version will cater for dependencies upgrades, bug fixes, security patches and co. -The versioning will remain like `v3.x.x` until further notice. +The versioning will remain like `v3.x.x` until further notice. The current version is **`v3.4.0`** -## Binaries and Minimum Go Versions +## ๐Ÿ“ฆ Binaries and Minimum Go Versions | From | To | Minimum Go Version | |----------|----------|--------------------| +| `v3.3.2` | `v3.4.0` | `1.23.0` | | `v2.0.2` | `v3.3.2` | `1.22.0` | | `v1.1.3` | `v2.0.1` | `1.21.0` | | `v1.0.0` | `v1.1.2` | `1.20.0` | -## Features +## โœจ Features ### Event Sourced Behavior @@ -171,24 +173,38 @@ That enables real-time processing of state without having to interact with the s Just use `Subscribe` method of [Engine](./engine.go) and start iterating through the messages and cast every message to the [DurableState](./protos/ego/v3/ego.proto). -## Cluster +### Publishers + +eGo offers the following publisher APIs: + +* [EventPublisher](./publisher.go) - publish `EventSourcedBehavior` events to any streaming platform +* [StatePublisher](./publisher.go) - publish `DurableStateBehavior` state to any streaming platform + +The following streaming connectors are implemented out of the box: + +* [Kafka](./publisher/kafka) +* [Pulsar](./publisher/pulsar) +* [NATs](./publisher/nats) +* [Websocket](./publisher/websocket) + +## ๐ŸŒ Cluster The cluster mode heavily relies on [Go-Akt](https://github.com/Tochemey/goakt#clustering) clustering. To enable clustering one need to use `WithCluster` option when creating the eGo engine. -## Testkit +## ๐Ÿงช Testkit eGo comes bundle with in-memory datastore that can be found in the [testkit](./testkit) package. This can help play with eGo. -## Mocks +## ๐Ÿ—๏ธ Mocks eGo ships in some [mocks](./mocks) that can help mock the data stores for unit tests purpose. -## Examples +## ๐ŸŽฏ Examples Check the [examples](./example) -## Sample +## ๐Ÿ“ Sample ```go package main @@ -335,6 +351,6 @@ func (a *AccountBehavior) HandleEvent(_ context.Context, event ego.Event, priorS ``` -## Contribution +## ๐Ÿคฒ Contribution kindly follow the instructions in the [contribution doc](./contributing.md) diff --git a/resources/durablestore_postgres.sql b/resources/durablestore_postgres.sql index 89629d2..523ee96 100644 --- a/resources/durablestore_postgres.sql +++ b/resources/durablestore_postgres.sql @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/resources/eventstore_postgres.sql b/resources/eventstore_postgres.sql index 5c26fc9..2681414 100644 --- a/resources/eventstore_postgres.sql +++ b/resources/eventstore_postgres.sql @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/resources/offsetstore_postgres.sql b/resources/offsetstore_postgres.sql index bd5b7c4..56cbc3c 100644 --- a/resources/offsetstore_postgres.sql +++ b/resources/offsetstore_postgres.sql @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/spawn_config.go b/spawn_config.go index b0cc0d1..1e3ea19 100644 --- a/spawn_config.go +++ b/spawn_config.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/spawn_config_test.go b/spawn_config_test.go index 32f9b29..4d86160 100644 --- a/spawn_config_test.go +++ b/spawn_config_test.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/testkit/durablestore.go b/testkit/durablestore.go index a287f02..18b6a1c 100644 --- a/testkit/durablestore.go +++ b/testkit/durablestore.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/testkit/eventstore.go b/testkit/eventstore.go index 9ff8d1d..3700252 100644 --- a/testkit/eventstore.go +++ b/testkit/eventstore.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -94,7 +94,7 @@ func (x *EventStore) Ping(ctx context.Context) error { } func (x *EventStore) DeleteEvents(_ context.Context, persistenceID string, toSequenceNumber uint64) error { - x.db.Range(func(key interface{}, _ interface{}) bool { + x.db.Range(func(key interface{}, _ any) bool { k := key.(EventKey) if k.PersistenceID == persistenceID && k.SequenceNumber <= toSequenceNumber { x.db.Delete(key) @@ -104,9 +104,9 @@ func (x *EventStore) DeleteEvents(_ context.Context, persistenceID string, toSeq return nil } -func (x *EventStore) ReplayEvents(_ context.Context, persistenceID string, fromSequenceNumber, toSequenceNumber uint64, max uint64) ([]*egopb.Event, error) { +func (x *EventStore) ReplayEvents(_ context.Context, persistenceID string, fromSequenceNumber, toSequenceNumber uint64, limit uint64) ([]*egopb.Event, error) { var events []*egopb.Event - collect := func(key, value interface{}) bool { + collect := func(key, value any) bool { k, ok := key.(EventKey) if !ok { return true // Skip if the key is not of type EventKey @@ -116,7 +116,7 @@ func (x *EventStore) ReplayEvents(_ context.Context, persistenceID string, fromS k.SequenceNumber >= fromSequenceNumber && k.SequenceNumber <= toSequenceNumber { events = append(events, value.(*egopb.Event)) - if len(events) >= int(max) { + if len(events) >= int(limit) { return false // Stop further iteration } } @@ -129,7 +129,7 @@ func (x *EventStore) ReplayEvents(_ context.Context, persistenceID string, fromS func (x *EventStore) GetLatestEvent(_ context.Context, persistenceID string) (*egopb.Event, error) { var events []*egopb.Event - x.db.Range(func(key interface{}, value interface{}) bool { + x.db.Range(func(key any, value any) bool { k := key.(EventKey) if k.PersistenceID == persistenceID { events = append(events, value.(*egopb.Event)) @@ -150,7 +150,7 @@ func (x *EventStore) GetLatestEvent(_ context.Context, persistenceID string) (*e func (x *EventStore) PersistenceIDs(_ context.Context, pageSize uint64, pageToken string) (persistenceIDs []string, nextPageToken string, err error) { // step 1: collect unique PersistenceIDs using a map idSet := make(map[string]struct{}) - x.db.Range(func(key, _ interface{}) bool { + x.db.Range(func(key, _ any) bool { k := key.(EventKey) idSet[k.PersistenceID] = struct{}{} return true @@ -193,9 +193,9 @@ func (x *EventStore) PersistenceIDs(_ context.Context, pageSize uint64, pageToke return persistenceIDs, nextPageToken, nil } -func (x *EventStore) GetShardEvents(_ context.Context, shardNumber uint64, offset int64, max uint64) ([]*egopb.Event, int64, error) { +func (x *EventStore) GetShardEvents(_ context.Context, shardNumber uint64, offset int64, limit uint64) ([]*egopb.Event, int64, error) { var shardEvents []*egopb.Event - x.db.Range(func(_ interface{}, value interface{}) bool { + x.db.Range(func(_ any, value any) bool { event := value.(*egopb.Event) if event.GetShard() == shardNumber { shardEvents = append(shardEvents, value.(*egopb.Event)) @@ -210,7 +210,7 @@ func (x *EventStore) GetShardEvents(_ context.Context, shardNumber uint64, offse var events []*egopb.Event for _, event := range shardEvents { if event.GetTimestamp() > offset { - if len(events) <= int(max) { + if len(events) <= int(limit) { events = append(events, event) } } @@ -230,7 +230,7 @@ func (x *EventStore) GetShardEvents(_ context.Context, shardNumber uint64, offse func (x *EventStore) ShardNumbers(context.Context) ([]uint64, error) { shards := goset.NewSet[uint64]() - x.db.Range(func(_ interface{}, value interface{}) bool { + x.db.Range(func(_ any, value any) bool { event := value.(*egopb.Event) shards.Add(event.GetShard()) return true diff --git a/testkit/offsetstore.go b/testkit/offsetstore.go index ccd350f..43db109 100644 --- a/testkit/offsetstore.go +++ b/testkit/offsetstore.go @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * Copyright (c) 2023-2025 Tochemey * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/tls.go b/tls.go new file mode 100644 index 0000000..97f7e12 --- /dev/null +++ b/tls.go @@ -0,0 +1,44 @@ +/* + * MIT License + * + * Copyright (c) 2022-2025 Arsene Tochemey Gandote + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package ego + +import "crypto/tls" + +// TLS defines the TLS settings for secure communication. +// It provides separate configurations for both client and server TLS settings. +// +// Ensure that both the server and client are configured with the same +// root Certificate Authority (CA) to enable a successful handshake +// and mutual authentication. +// +// Fields: +// - ClientTLS: The TLS configuration for the client. +// - ServerTLS: The TLS configuration for the server. +type TLS struct { + // ClientTLS defines the TLS configuration for the client. + ClientTLS *tls.Config + // ServerTLS defines the TLS configuration for the server. + ServerTLS *tls.Config +}