Repository: Tochemey/goakt
Pull request #324 (2024-05-19 11:56:04)

refactor: enhance clustering options
- `WithClustering` accept additional parameters to setup the cluster mode
- Better cluster provider configuration
- Host node information is no longer fetched from hidden env vars
- Dependencies upgrade

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/Tochemey/goakt/pull/324?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Arsene) Report
Attention: Patch coverage is `67.34694%` with `16 lines` in your changes are missing coverage. Please review.
> Project coverage is 87.00%. Comparing base [(`6f6452f`)](https://app.codecov.io/gh/Tochemey/goakt/commit/6f6452fd50a47e098ab5416aa088083a2697aaec?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Arsene) to head [(`672b04a`)](https://app.codecov.io/gh/Tochemey/goakt/pull/324?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Arsene).

| [Files](https://app.codecov.io/gh/Tochemey/goakt/pull/324?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Arsene) | Patch % | Lines |
|---|---|---|
| [actors/actor\_system.go](https://app.codecov.io/gh/Tochemey/goakt/pull/324?src=pr&el=tree&filepath=actors%2Factor_system.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Arsene#diff-YWN0b3JzL2FjdG9yX3N5c3RlbS5nbw==) | 60.86% | [7 Missing and 2 partials :warning: ](https://app.codecov.io/gh/Tochemey/goakt/pull/324?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Arsene) |
| [actors/pid\_map.go](https://app.codecov.io/gh/Tochemey/goakt/pull/324?src=pr&el=tree&filepath=actors%2Fpid_map.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Arsene#diff-YWN0b3JzL3BpZF9tYXAuZ28=) | 61.11% | [5 Missing and 2 partials :warning: ](https://app.codecov.io/gh/Tochemey/goakt/pull/324?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Arsene) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #324      +/-   ##
==========================================
+ Coverage   86.75%   87.00%   +0.24%     
==========================================
  Files          32       32              
  Lines        2998     2985      -13     
==========================================
- Hits         2601     2597       -4     
+ Misses        285      274      -11     
- Partials      112      114       +2     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/Tochemey/goakt/pull/324?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Arsene).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Arsene).


Patch:
diff --git a/README.md b/README.md
index 1795aad..21fa002 100644
--- a/README.md
+++ b/README.md
@@ -199,7 +199,7 @@ You can schedule sending messages to actor that will be acted upon in the future
 #### Cron Expression Format
 
 | Field        | Required | Allowed Values  | Allowed Special Characters |
-|--------------|----------|-----------------|----------------------------|
+| ------------ | -------- | --------------- | -------------------------- |
 | Seconds      | yes      | 0-59            | , - * /                    |
 | Minutes      | yes      | 0-59            | , - * /                    |
 | Hours        | yes      | 0-23            | , - * /                    |
@@ -382,7 +382,7 @@ const (
     applicationName = "accounts"
     actorSystemName    = "AccountsSystem"
     gossipPortName     = "gossip-port"
-    clusterPortName    = "cluster-port"
+    peersPortName    = "peers-port"
     remotingPortName   = "remoting-port"
 )
 // define the discovery config
@@ -392,7 +392,7 @@ config := kubernetes.Config{
     Namespace:        namespace,
     GossipPortName:   gossipPortName,
     RemotingPortName: remotingPortName,
-    ClusterPortName:  clusterPortName,
+    PeersPortName:  peersPortName,
 }
 
 // instantiate the k8 discovery provider
diff --git a/actors/actor_system.go b/actors/actor_system.go
index 888765b..dd70e24 100644
--- a/actors/actor_system.go
+++ b/actors/actor_system.go
@@ -35,6 +35,7 @@ import (
 
 	"connectrpc.com/connect"
 	"connectrpc.com/otelconnect"
+	"github.com/alphadose/haxmap"
 	"github.com/google/uuid"
 	"github.com/pkg/errors"
 	"go.opentelemetry.io/otel/codes"
@@ -180,10 +181,11 @@ type actorSystem struct {
 	// define the number of partitions to shard the actors in the cluster
 	partitionsCount uint64
 	// cluster mode
-	cluster     cluster.Interface
-	clusterChan chan *internalpb.WireActor
-	clusterPort int
-	gossipPort  int
+	cluster            cluster.Interface
+	clusterChan        chan *internalpb.WireActor
+	peersPort          int
+	gossipPort         int
+	minimumPeersQuorum uint16
 
 	partitionHasher hash.Hasher
 
@@ -214,6 +216,8 @@ type actorSystem struct {
 
 	registry   types.Registry
 	reflection reflection
+
+	peersCache *haxmap.Map[string, []byte]
 }
 
 // enforce compilation error when all methods of the ActorSystem interface are not implemented
@@ -230,7 +234,7 @@ func NewActorSystem(name string, opts ...Option) (ActorSystem, error) {
 	}
 
 	system := &actorSystem{
-		actors:              newPIDMap(10),
+		actors:              newPIDMap(1_000), // TODO need to check with memory footprint here since we change the map engine
 		clusterChan:         make(chan *internalpb.WireActor, 10),
 		name:                name,
 		logger:              log.DefaultLogger,
@@ -250,6 +254,8 @@ func NewActorSystem(name string, opts ...Option) (ActorSystem, error) {
 		eventsChan:          make(chan *cluster.Event, 1),
 		registry:            types.NewRegistry(),
 		clusterSyncStopSig:  make(chan types.Unit, 1),
+		minimumPeersQuorum:  1,
+		peersCache:          haxmap.New[string, []byte](100), // TODO need to check with memory footprint here since we change the map engine
 	}
 
 	system.started.Store(false)
@@ -754,9 +760,6 @@ func (x *actorSystem) Start(ctx context.Context) error {
 		if err := x.enableClustering(spanCtx); err != nil {
 			return err
 		}
-		// start cluster synchronization
-		// TODO: revisit this
-		// go x.runClusterSync()
 	}
 
 	x.scheduler.Start(spanCtx)
@@ -828,6 +831,7 @@ func (x *actorSystem) Stop(ctx context.Context) error {
 		}
 	}
 
+	x.actors.close()
 	x.reset()
 	return nil
 }
@@ -864,11 +868,11 @@ func (x *actorSystem) RemoteAsk(ctx context.Context, stream *connect.BidiStream[
 	}
 
 	for {
-		switch ctx.Err() {
-		case context.Canceled:
-			return connect.NewError(connect.CodeCanceled, ctx.Err())
-		case context.DeadlineExceeded:
-			return connect.NewError(connect.CodeDeadlineExceeded, ctx.Err())
+		switch err := ctx.Err(); {
+		case errors.Is(err, context.Canceled):
+			return connect.NewError(connect.CodeCanceled, err)
+		case errors.Is(err, context.DeadlineExceeded):
+			return connect.NewError(connect.CodeDeadlineExceeded, err)
 		}
 
 		request, err := stream.Receive()
@@ -1074,16 +1078,17 @@ func (x *actorSystem) enableClustering(ctx context.Context) error {
 		Name:         x.name,
 		Host:         x.remotingHost,
 		GossipPort:   x.gossipPort,
-		ClusterPort:  x.clusterPort,
+		PeersPort:    x.peersPort,
 		RemotingPort: int(x.remotingPort),
 	}
 
-	cluster, err := cluster.NewNode(x.Name(),
+	cluster, err := cluster.NewEngine(x.Name(),
 		x.discoveryProvider,
 		&hostNode,
 		cluster.WithLogger(x.logger),
 		cluster.WithPartitionsCount(x.partitionsCount),
 		cluster.WithHasher(x.partitionHasher),
+		cluster.WithMinimumPeersQuorum(x.minimumPeersQuorum),
 	)
 	if err != nil {
 		x.logger.Error(errors.Wrap(err, "failed to initialize cluster engine"))
@@ -1193,19 +1198,6 @@ func (x *actorSystem) reset() {
 	x.cluster = nil
 }
 
-// broadcast publishes newly created actor into the cluster when cluster is enabled
-func (x *actorSystem) broadcast(ctx context.Context) {
-	for wireActor := range x.clusterChan {
-		if x.cluster != nil {
-			if err := x.cluster.PutActor(ctx, wireActor); err != nil {
-				x.logger.Error(err.Error())
-				// TODO: stop or continue
-				return
-			}
-		}
-	}
-}
-
 // housekeeper time to time removes dead actors from the system
 // that helps free non-utilized resources
 func (x *actorSystem) housekeeper() {
@@ -1256,13 +1248,25 @@ func (x *actorSystem) registerMetrics() error {
 	return err
 }
 
+// broadcast publishes newly created actor into the cluster when cluster is enabled
+func (x *actorSystem) broadcast(ctx context.Context) {
+	for wireActor := range x.clusterChan {
+		if x.cluster != nil {
+			// set the actor in the cluster
+			if err := x.cluster.PutActor(ctx, wireActor); err != nil {
+				x.logger.Panic(err.Error())
+			}
+		}
+	}
+}
+
 // broadcastClusterEvents listens to cluster events and send them to the event streams
 func (x *actorSystem) broadcastClusterEvents() {
 	for event := range x.eventsChan {
 		if x.clusterEnabled.Load() {
 			if event != nil && event.Payload != nil {
 				// first need to resync actors map back to the cluster
-				x.clusterSync()
+				x.syncActors()
 				// push the event to the event stream
 				message, _ := event.Payload.UnmarshalNew()
 				if x.eventsStream != nil {
@@ -1275,12 +1279,12 @@ func (x *actorSystem) broadcastClusterEvents() {
 	}
 }
 
-// clusterSync synchronizes the node' actors map to the cluster.
-func (x *actorSystem) clusterSync() {
-	typesMap := x.actors.props()
-	if len(typesMap) != 0 {
+// syncActors synchronizes the node' actors map to the cluster.
+func (x *actorSystem) syncActors() {
+	props := x.actors.props()
+	if len(props) != 0 {
 		x.logger.Info("syncing node actors map to the cluster...")
-		for actorID, prop := range typesMap {
+		for actorID, actorProp := range props {
 			actorPath := NewPath(actorID, NewAddress(x.name, "", -1))
 			if x.remotingEnabled.Load() {
 				actorPath = NewPath(actorID, NewAddress(x.name, x.remotingHost, int(x.remotingPort)))
@@ -1294,32 +1298,9 @@ func (x *actorSystem) clusterSync() {
 				ActorName:    actorID,
 				ActorAddress: actorPath.RemoteAddress(),
 				ActorPath:    actorPath.String(),
-				ActorType:    prop.rtype.Name(),
+				ActorType:    actorProp.rtype.Name(),
 			}
 		}
 		x.logger.Info("node actors map successfully synced back to the cluster.")
 	}
 }
-
-// runClusterSync runs time to time cluster synchronization
-// by populating the given node actors map to the cluster for availability
-func (x *actorSystem) runClusterSync() {
-	x.logger.Info("cluster synchronization has started...")
-	ticker := time.NewTicker(5 * time.Minute)
-	tickerStopSig := make(chan types.Unit, 1)
-	go func() {
-		for {
-			select {
-			case <-ticker.C:
-				x.clusterSync()
-			case <-x.clusterSyncStopSig:
-				tickerStopSig <- types.Unit{}
-				return
-			}
-		}
-	}()
-
-	<-tickerStopSig
-	ticker.Stop()
-	x.logger.Info("cluster synchronization has stopped...")
-}
diff --git a/actors/option.go b/actors/option.go
index 9cf26c7..6b2036c 100644
--- a/actors/option.go
+++ b/actors/option.go
@@ -113,14 +113,15 @@ func WithRemoting(host string, port int32) Option {
 	})
 }
 
-// WithClustering enables clustering on the actor system.
-func WithClustering(provider discovery.Provider, partitionCount uint64, gossipPort, clusterPort int) Option {
+// WithClustering enables the cluster mode.
+func WithClustering(provider discovery.Provider, partitionCount uint64, minimumPeersQuorum uint16, gossipPort, peersPort int) Option {
 	return OptionFunc(func(a *actorSystem) {
 		a.clusterEnabled.Store(true)
 		a.partitionsCount = partitionCount
 		a.discoveryProvider = provider
-		a.clusterPort = clusterPort
+		a.peersPort = peersPort
 		a.gossipPort = gossipPort
+		a.minimumPeersQuorum = minimumPeersQuorum
 	})
 }
 
diff --git a/actors/pid_map.go b/actors/pid_map.go
index 434d8aa..60d51db 100644
--- a/actors/pid_map.go
+++ b/actors/pid_map.go
@@ -26,8 +26,8 @@ package actors
 
 import (
 	"reflect"
-	"sync"
-	"sync/atomic"
+
+	"github.com/alphadose/haxmap"
 
 	"github.com/tochemey/goakt/internal/types"
 )
@@ -38,38 +38,32 @@ type prop struct {
 }
 
 type pidMap struct {
-	mu       *sync.RWMutex
-	size     atomic.Int64
-	propsMap map[string]*prop
+	mappings *haxmap.Map[string, *prop]
 }
 
 func newPIDMap(cap int) *pidMap {
 	return &pidMap{
-		mu:       &sync.RWMutex{},
-		propsMap: make(map[string]*prop, cap),
+		mappings: haxmap.New[string, *prop](uintptr(cap)),
 	}
 }
 
 // len returns the number of PIDs
 func (m *pidMap) len() int {
-	return int(m.size.Load())
+	return int(m.mappings.Len())
 }
 
 // get retrieves a pid by its address
 func (m *pidMap) get(path *Path) (pid PID, ok bool) {
-	m.mu.RLock()
-	prop, ok := m.propsMap[path.String()]
+	prop, ok := m.mappings.Get(path.String())
 	if prop != nil {
 		pid = prop.pid
 		ok = true
 	}
-	m.mu.RUnlock()
 	return
 }
 
 // set sets a pid in the map
 func (m *pidMap) set(pid PID) {
-	m.mu.Lock()
 	if pid != nil {
 		var rtype reflect.Type
 		handle := pid.ActorHandle()
@@ -77,37 +71,43 @@ func (m *pidMap) set(pid PID) {
 			rtype = types.RuntimeTypeOf(handle)
 		}
 
-		m.propsMap[pid.ActorPath().String()] = &prop{
+		m.mappings.Set(pid.ActorPath().String(), &prop{
 			pid:   pid,
 			rtype: rtype,
-		}
-		m.size.Add(1)
-		m.mu.Unlock()
+		})
 	}
 }
 
 // delete removes a pid from the map
 func (m *pidMap) delete(addr *Path) {
-	m.mu.Lock()
-	delete(m.propsMap, addr.String())
-	m.size.Add(-1)
-	m.mu.Unlock()
+	m.mappings.Del(addr.String())
 }
 
 // pids returns all actors as a slice
 func (m *pidMap) pids() []PID {
-	m.mu.Lock()
 	var out []PID
-	for _, prop := range m.propsMap {
+	m.mappings.ForEach(func(_ string, prop *prop) bool {
+		if len(out) == int(m.mappings.Len()) {
+			return false
+		}
 		out = append(out, prop.pid)
-	}
-	m.mu.Unlock()
+		return true
+	})
 	return out
 }
 
 func (m *pidMap) props() map[string]*prop {
-	m.mu.Lock()
-	out := m.propsMap
-	m.mu.Unlock()
+	out := make(map[string]*prop, m.mappings.Len())
+	m.mappings.ForEach(func(key string, prop *prop) bool {
+		if len(out) == int(m.mappings.Len()) {
+			return false
+		}
+		out[key] = prop
+		return true
+	})
 	return out
 }
+
+func (m *pidMap) close() {
+	m.mappings.Clear()
+}
diff --git a/discovery/kubernetes/config.go b/discovery/kubernetes/config.go
index b04542a..4046c88 100644
--- a/discovery/kubernetes/config.go
+++ b/discovery/kubernetes/config.go
@@ -38,8 +38,8 @@ type Config struct {
 	GossipPortName string
 	// RemotingPortName specifies the remoting port name
 	RemotingPortName string
-	// ClusterPortName specifies the cluster port name
-	ClusterPortName string
+	// PeersPortName specifies the cluster port name
+	PeersPortName string
 	// ActorSystemName specifies the given actor system name
 	ActorSystemName string
 }
@@ -50,7 +50,7 @@ func (x Config) Validate() error {
 		AddValidator(validation.NewEmptyStringValidator("Namespace", x.Namespace)).
 		AddValidator(validation.NewEmptyStringValidator("ApplicationName", x.ApplicationName)).
 		AddValidator(validation.NewEmptyStringValidator("GossipPortName", x.GossipPortName)).
-		AddValidator(validation.NewEmptyStringValidator("ClusterPortName", x.ClusterPortName)).
+		AddValidator(validation.NewEmptyStringValidator("PeersPortName", x.PeersPortName)).
 		AddValidator(validation.NewEmptyStringValidator("RemotingPortName", x.RemotingPortName)).
 		AddValidator(validation.NewEmptyStringValidator("ActorSystemName", x.ActorSystemName)).
 		Validate()
diff --git a/discovery/kubernetes/discovery.go b/discovery/kubernetes/discovery.go
index 0d01af8..b0244e0 100644
--- a/discovery/kubernetes/discovery.go
+++ b/discovery/kubernetes/discovery.go
@@ -147,7 +147,7 @@ func (d *Discovery) DiscoverPeers() ([]string, error) {
 		return nil, err
 	}
 
-	validPortNames := []string{d.config.ClusterPortName, d.config.GossipPortName, d.config.RemotingPortName}
+	validPortNames := []string{d.config.PeersPortName, d.config.GossipPortName, d.config.RemotingPortName}
 
 	// define the addresses list
 	addresses := goset.NewSet[string]()
diff --git a/discovery/node.go b/discovery/node.go
index c451b68..82f6149 100644
--- a/discovery/node.go
+++ b/discovery/node.go
@@ -37,15 +37,15 @@ type Node struct {
 	Host string
 	// GossipPort
 	GossipPort int
-	// ClusterPort
-	ClusterPort int
+	// PeersPort
+	PeersPort int
 	// RemotingPort
 	RemotingPort int
 }
 
-// ClusterAddress returns address the node's peers will use to connect to
-func (n Node) ClusterAddress() string {
-	return net.JoinHostPort(n.Host, strconv.Itoa(n.ClusterPort))
+// PeersAddress returns address the node's peers will use to connect to
+func (n Node) PeersAddress() string {
+	return net.JoinHostPort(n.Host, strconv.Itoa(n.PeersPort))
 }
 
 // GossipAddress returns the node discovery address
diff --git a/examples/actor-cluster/dnssd/cmd/run.go b/examples/actor-cluster/dnssd/cmd/run.go
index 9f7a9cf..d1fbf79 100644
--- a/examples/actor-cluster/dnssd/cmd/run.go
+++ b/examples/actor-cluster/dnssd/cmd/run.go
@@ -140,7 +140,7 @@ var runCmd = &cobra.Command{
 			goakt.WithActorInitMaxRetries(3),
 			goakt.WithTracing(),
 			goakt.WithRemoting(host, int32(config.RemotingPort)),
-			goakt.WithClustering(disco, 20, config.GossipPort, config.ClusterPort))
+			goakt.WithClustering(disco, 20, 1, config.GossipPort, config.PeersPort))
 		// handle the error
 		if err != nil {
 			logger.Panic(err)
diff --git a/examples/actor-cluster/dnssd/service/config.go b/examples/actor-cluster/dnssd/service/config.go
index 2cd4c9a..15fc16d 100644
--- a/examples/actor-cluster/dnssd/service/config.go
+++ b/examples/actor-cluster/dnssd/service/config.go
@@ -33,7 +33,7 @@ type Config struct {
 	ActorSystemName string `env:"SYSTEM_NAME"`
 	TraceURL        string `env:"TRACE_URL"`
 	GossipPort      int    `env:"GOSSIP_PORT"`
-	ClusterPort     int    `env:"CLUSTER_PORT"`
+	PeersPort       int    `env:"CLUSTER_PORT"`
 	RemotingPort    int    `env:"REMOTING_PORT"`
 }
 
diff --git a/examples/actor-cluster/k8s/cmd/run.go b/examples/actor-cluster/k8s/cmd/run.go
index ed5ba35..18eb984 100644
--- a/examples/actor-cluster/k8s/cmd/run.go
+++ b/examples/actor-cluster/k8s/cmd/run.go
@@ -45,7 +45,7 @@ const (
 	applicationName  = "accounts"
 	actorSystemName  = "AccountsSystem"
 	gossipPortName   = "gossip-port"
-	clusterPortName  = "cluster-port"
+	peersPortName    = "peers-port"
 	remotingPortName = "remoting-port"
 )
 
@@ -84,7 +84,7 @@ var runCmd = &cobra.Command{
 			Namespace:        namespace,
 			GossipPortName:   gossipPortName,
 			RemotingPortName: remotingPortName,
-			ClusterPortName:  clusterPortName,
+			PeersPortName:    peersPortName,
 		})
 
 		// get the port config
@@ -100,7 +100,7 @@ var runCmd = &cobra.Command{
 			goakt.WithLogger(logger),
 			goakt.WithActorInitMaxRetries(3),
 			goakt.WithRemoting(host, int32(config.RemotingPort)),
-			goakt.WithClustering(disco, 20, config.GossipPort, config.ClusterPort))
+			goakt.WithClustering(disco, 20, 1, config.GossipPort, config.ClusterPort))
 		// handle the error
 		if err != nil {
 			logger.Panic(err)
diff --git a/examples/actor-cluster/k8s/k8s.yaml b/examples/actor-cluster/k8s/k8s.yaml
index ba42efa..2374145 100644
--- a/examples/actor-cluster/k8s/k8s.yaml
+++ b/examples/actor-cluster/k8s/k8s.yaml
@@ -59,7 +59,7 @@ spec:
               name: gossip-port
               protocol: TCP
             - containerPort: 3320
-              name: cluster-port
+              name: peers-port
               protocol: TCP
           env:
             - name: NODE_NAME
@@ -72,7 +72,7 @@ spec:
                   fieldPath: status.podIP
             - name: GOSSIP_PORT
               value: "3322"
-            - name: CLUSTER_PORT
+            - name: PEERS_PORT
               value: "3320"
             - name: REMOTING_PORT
               value: "50052"
diff --git a/go.mod b/go.mod
index b9923f6..a79ca79 100644
--- a/go.mod
+++ b/go.mod
@@ -3,41 +3,42 @@ module github.com/tochemey/goakt
 go 1.21
 
 require (
-	connectrpc.com/connect v1.16.1
+	connectrpc.com/connect v1.16.2
 	connectrpc.com/otelconnect v0.7.0
-	github.com/buraksezer/olric v0.5.6-0.20240205222928-c5efb0d4b5ea
+	github.com/alphadose/haxmap v1.4.0
+	github.com/buraksezer/olric v0.5.6-0.20240510193155-81e12546eb39
 	github.com/caarlos0/env/v11 v11.0.0
-	github.com/cespare/xxhash/v2 v2.3.0
 	github.com/deckarep/golang-set/v2 v2.6.0
 	github.com/flowchartsman/retry v1.2.0
 	github.com/google/go-cmp v0.6.0
 	github.com/google/uuid v1.6.0
 	github.com/grandcat/zeroconf v1.0.0
 	github.com/joho/godotenv v1.5.1
-	github.com/nats-io/nats-server/v2 v2.10.14
-	github.com/nats-io/nats.go v1.34.1
+	github.com/nats-io/nats-server/v2 v2.10.15
+	github.com/nats-io/nats.go v1.35.0
 	github.com/pkg/errors v0.9.1
-	github.com/prometheus/client_golang v1.19.0
+	github.com/prometheus/client_golang v1.19.1
 	github.com/redis/go-redis/v9 v9.5.1
-	github.com/reugn/go-quartz v0.11.2
+	github.com/reugn/go-quartz v0.12.0
 	github.com/spf13/cobra v1.8.0
 	github.com/stretchr/testify v1.9.0
 	github.com/travisjeffery/go-dynaport v1.0.0
-	go.opentelemetry.io/otel v1.25.0
-	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.25.0
-	go.opentelemetry.io/otel/exporters/prometheus v0.47.0
-	go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.25.0
-	go.opentelemetry.io/otel/metric v1.25.0
-	go.opentelemetry.io/otel/sdk v1.25.0
-	go.opentelemetry.io/otel/sdk/metric v1.25.0
-	go.opentelemetry.io/otel/trace v1.25.0
+	github.com/zeebo/xxh3 v1.0.2
+	go.opentelemetry.io/otel v1.26.0
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.26.0
+	go.opentelemetry.io/otel/exporters/prometheus v0.48.0
+	go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.26.0
+	go.opentelemetry.io/otel/metric v1.26.0
+	go.opentelemetry.io/otel/sdk v1.26.0
+	go.opentelemetry.io/otel/sdk/metric v1.26.0
+	go.opentelemetry.io/otel/trace v1.26.0
 	go.uber.org/atomic v1.11.0
 	go.uber.org/goleak v1.3.0
 	go.uber.org/multierr v1.11.0
 	go.uber.org/zap v1.27.0
-	golang.org/x/net v0.24.0
+	golang.org/x/net v0.25.0
 	golang.org/x/sync v0.7.0
-	google.golang.org/protobuf v1.33.0
+	google.golang.org/protobuf v1.34.1
 	k8s.io/api v0.29.4
 	k8s.io/apimachinery v0.29.4
 	k8s.io/client-go v0.29.4
@@ -45,55 +46,56 @@ require (
 )
 
 require (
-	github.com/RoaringBitmap/roaring v1.7.0 // indirect
+	github.com/RoaringBitmap/roaring v1.9.3 // 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.13.0 // indirect
 	github.com/buraksezer/consistent v0.10.0 // indirect
 	github.com/cenkalti/backoff v2.2.1+incompatible // indirect
 	github.com/cenkalti/backoff/v4 v4.3.0 // indirect
+	github.com/cespare/xxhash/v2 v2.3.0 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
-	github.com/emicklei/go-restful/v3 v3.11.0 // indirect
-	github.com/evanphx/json-patch v5.6.0+incompatible // indirect
+	github.com/emicklei/go-restful/v3 v3.12.0 // indirect
+	github.com/evanphx/json-patch v5.9.0+incompatible // indirect
 	github.com/go-logr/logr v1.4.1 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
-	github.com/go-openapi/jsonpointer v0.20.0 // indirect
-	github.com/go-openapi/jsonreference v0.20.2 // indirect
-	github.com/go-openapi/swag v0.22.4 // indirect
+	github.com/go-openapi/jsonpointer v0.21.0 // indirect
+	github.com/go-openapi/jsonreference v0.21.0 // indirect
+	github.com/go-openapi/swag v0.23.0 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang/protobuf v1.5.4 // indirect
 	github.com/google/btree v1.1.2 // indirect
 	github.com/google/gnostic-models v0.6.8 // indirect
 	github.com/google/gofuzz v1.2.0 // indirect
-	github.com/google/pprof v0.0.0-20230912144702-c363fe2c2ed8 // indirect
-	github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
+	github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
 	github.com/hashicorp/errwrap v1.1.0 // indirect
 	github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
-	github.com/hashicorp/go-msgpack v0.5.5 // 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.6 // indirect
 	github.com/hashicorp/golang-lru v1.0.2 // indirect
 	github.com/hashicorp/logutils v1.0.0 // indirect
-	github.com/hashicorp/memberlist v0.5.0 // indirect
+	github.com/hashicorp/memberlist v0.5.1 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
-	github.com/klauspost/compress v1.17.7 // indirect
+	github.com/klauspost/compress v1.17.8 // indirect
+	github.com/klauspost/cpuid/v2 v2.2.7 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
-	github.com/miekg/dns v1.1.58 // indirect
+	github.com/miekg/dns v1.1.59 // indirect
 	github.com/minio/highwayhash v1.0.2 // 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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
-	github.com/nats-io/jwt/v2 v2.5.5 // indirect
+	github.com/nats-io/jwt/v2 v2.5.7 // indirect
 	github.com/nats-io/nkeys v0.4.7 // indirect
 	github.com/nats-io/nuid v1.0.1 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/prometheus/client_model v0.6.1 // indirect
-	github.com/prometheus/common v0.48.0 // indirect
-	github.com/prometheus/procfs v0.12.0 // indirect
+	github.com/prometheus/common v0.53.0 // indirect
+	github.com/prometheus/procfs v0.15.0 // indirect
 	github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
 	github.com/spf13/pflag v1.0.5 // indirect
 	github.com/stretchr/objx v0.5.2 // indirect
@@ -102,26 +104,26 @@ require (
 	github.com/tidwall/redcon v1.6.2 // indirect
 	github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
 	github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
-	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0 // indirect
-	go.opentelemetry.io/proto/otlp v1.1.0 // indirect
-	golang.org/x/crypto v0.22.0 // indirect
-	golang.org/x/mod v0.14.0 // indirect
-	golang.org/x/oauth2 v0.17.0 // indirect
-	golang.org/x/sys v0.19.0 // indirect
-	golang.org/x/term v0.19.0 // indirect
-	golang.org/x/text v0.14.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.26.0 // indirect
+	go.opentelemetry.io/proto/otlp v1.2.0 // indirect
+	golang.org/x/crypto v0.23.0 // indirect
+	golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
+	golang.org/x/mod v0.17.0 // indirect
+	golang.org/x/oauth2 v0.20.0 // indirect
+	golang.org/x/sys v0.20.0 // indirect
+	golang.org/x/term v0.20.0 // indirect
+	golang.org/x/text v0.15.0 // indirect
 	golang.org/x/time v0.5.0 // indirect
-	golang.org/x/tools v0.17.0 // indirect
-	google.golang.org/appengine v1.6.8 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect
-	google.golang.org/grpc v1.63.0 // indirect
+	golang.org/x/tools v0.21.0 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20240515191416-fc5f0ca64291 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 // indirect
+	google.golang.org/grpc v1.64.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/klog/v2 v2.110.1 // indirect
-	k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
+	k8s.io/klog/v2 v2.120.1 // indirect
+	k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f // indirect
 	sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
 	sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
-	sigs.k8s.io/yaml v1.3.0 // indirect
+	sigs.k8s.io/yaml v1.4.0 // indirect
 )
diff --git a/go.sum b/go.sum
index e0e3379..58f9faa 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,5 @@
-connectrpc.com/connect v1.16.1 h1:rOdrK/RTI/7TVnn3JsVxt3n028MlTRwmK5Q4heSpjis=
-connectrpc.com/connect v1.16.1/go.mod h1:XpZAduBQUySsb4/KO5JffORVkDI4B6/EYPi7N8xpNZw=
+connectrpc.com/connect v1.16.2 h1:ybd6y+ls7GOlb7Bh5C8+ghA6SvCBajHwxssO2CGFjqE=
+connectrpc.com/connect v1.16.2/go.mod h1:n2kgwskMHXC+lVqb18wngEpF95ldBHXjZYJussz5FRc=
 connectrpc.com/otelconnect v0.7.0 h1:ZH55ZZtcJOTKWWLy3qmL4Pam4RzRWBJFOqTPyAqCXkY=
 connectrpc.com/otelconnect v0.7.0/go.mod h1:Bt2ivBymHZHqxvo4HkJ0EwHuUzQN6k2l0oH+mp/8nwc=
 github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
@@ -7,12 +7,14 @@ github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy86
 github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
 github.com/Masterminds/sprig/v3 v3.2.1/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
 github.com/RoaringBitmap/roaring v1.6.0/go.mod h1:plvDsJQpxOC5bw8LRteu/MLWHsHez/3y6cubLI4/1yE=
-github.com/RoaringBitmap/roaring v1.7.0 h1:OZF303tJCER1Tj3x+aArx/S5X7hrT186ri6JjrGvG68=
-github.com/RoaringBitmap/roaring v1.7.0/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90=
+github.com/RoaringBitmap/roaring v1.9.3 h1:t4EbC5qQwnisr5PrP9nt0IRhRTb9gMUgQF4t4S2OByM=
+github.com/RoaringBitmap/roaring v1.9.3/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90=
 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/alphadose/haxmap v1.4.0 h1:1yn+oGzy2THJj1DMuJBzRanE3sMnDAjJVbU0L31Jp3w=
+github.com/alphadose/haxmap v1.4.0/go.mod h1:rjHw1IAqbxm0S3U5tD16GoKsiAd8FWx5BJ2IYqXwgmM=
 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
 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=
@@ -32,8 +34,8 @@ 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/buraksezer/olric v0.5.6-0.20240205222928-c5efb0d4b5ea h1:iThz84rs8fghn9bRJLMxJjT6CAjIa6uCr/B2H6+qocY=
-github.com/buraksezer/olric v0.5.6-0.20240205222928-c5efb0d4b5ea/go.mod h1:b1aCKANfqosRKIeYR5NPETD8/VnxcVdlbYGMZ97GPNA=
+github.com/buraksezer/olric v0.5.6-0.20240510193155-81e12546eb39 h1:ZB2KxErCucUiYTz9gZlujrurj4WcOe+j4t1sB1okuGw=
+github.com/buraksezer/olric v0.5.6-0.20240510193155-81e12546eb39/go.mod h1:b1aCKANfqosRKIeYR5NPETD8/VnxcVdlbYGMZ97GPNA=
 github.com/caarlos0/env/v11 v11.0.0 h1:ZIlkOjuL3xoZS0kmUJlF74j2Qj8GMOq3CDLX/Viak8Q=
 github.com/caarlos0/env/v11 v11.0.0/go.mod h1:2RC3HQu8BQqtEK3V4iHPxj0jOdWdbPpWJ6pOueeU1xM=
 github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
@@ -47,7 +49,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
 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/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
-github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -55,10 +56,10 @@ github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80N
 github.com/deckarep/golang-set/v2 v2.6.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.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
-github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
-github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U=
-github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
+github.com/emicklei/go-restful/v3 v3.12.0 h1:y2DdzBAURM29NFF94q6RaY4vjIH1rtwDapwQtU84iWk=
+github.com/emicklei/go-restful/v3 v3.12.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls=
+github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/flowchartsman/retry v1.2.0 h1:qDhlw6RNufXz6RGr+IiYimFpMMkt77SUSHY5tgFaUCU=
 github.com/flowchartsman/retry v1.2.0/go.mod h1:+sfx8OgCCiAr3t5jh2Gk+T0fRTI+k52edaYxURQxY64=
@@ -67,30 +68,26 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
 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-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
 github.com/go-logr/logr v1.4.1/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-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
-github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ=
-github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA=
-github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
-github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
-github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
-github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
-github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
+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/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
-github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
+github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
+github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
 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.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 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 v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@@ -100,23 +97,22 @@ github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvR
 github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
 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.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 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/pprof v0.0.0-20230912144702-c363fe2c2ed8 h1:gpptm606MZYGaMHMsB4Srmb6EbW/IVHnt04rcMXnkBQ=
-github.com/google/pprof v0.0.0-20230912144702-c363fe2c2ed8/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
+github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
+github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 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/grandcat/zeroconf v1.0.0 h1:uHhahLBKqwWBV6WZUDAT71044vwOTL+McW0mBJvo6kE=
 github.com/grandcat/zeroconf v1.0.0/go.mod h1:lTKmG1zh86XyCoUeIHSA4FJMBwCJiQmGfcP2PdzytEs=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
 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=
@@ -125,8 +121,8 @@ github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjh
 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-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
-github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
-github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+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.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
 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=
@@ -142,8 +138,9 @@ github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iP
 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.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM=
 github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0=
+github.com/hashicorp/memberlist v0.5.1 h1:mk5dRuzeDNis2bi6LLoQIXfMH7JQvAzt3mQD0vNZZUo=
+github.com/hashicorp/memberlist v0.5.1/go.mod h1:zGDXV6AqbDTKTM6yxW0I4+JtFzZAJVoIPvss4hV8F24=
 github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
 github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
 github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
@@ -160,12 +157,13 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 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.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
-github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
+github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
+github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
+github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
+github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/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.1/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=
@@ -180,8 +178,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
 github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
 github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
 github.com/miekg/dns v1.1.45/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
-github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
-github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
+github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
+github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
 github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
 github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
 github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4=
@@ -200,20 +198,20 @@ github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOl
 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/nats-io/jwt/v2 v2.5.5 h1:ROfXb50elFq5c9+1ztaUbdlrArNFl2+fQWP6B8HGEq4=
-github.com/nats-io/jwt/v2 v2.5.5/go.mod h1:ZdWS1nZa6WMZfFwwgpEaqBV8EPGVgOTDHN/wTbz0Y5A=
-github.com/nats-io/nats-server/v2 v2.10.14 h1:98gPJFOAO2vLdM0gogh8GAiHghwErrSLhugIqzRC+tk=
-github.com/nats-io/nats-server/v2 v2.10.14/go.mod h1:a0TwOVBJZz6Hwv7JH2E4ONdpyFk9do0C18TEwxnHdRk=
-github.com/nats-io/nats.go v1.34.1 h1:syWey5xaNHZgicYBemv0nohUPPmaLteiBEUT6Q5+F/4=
-github.com/nats-io/nats.go v1.34.1/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
+github.com/nats-io/jwt/v2 v2.5.7 h1:j5lH1fUXCnJnY8SsQeB/a/z9Azgu2bYIDvtPVNdxe2c=
+github.com/nats-io/jwt/v2 v2.5.7/go.mod h1:ZdWS1nZa6WMZfFwwgpEaqBV8EPGVgOTDHN/wTbz0Y5A=
+github.com/nats-io/nats-server/v2 v2.10.15 h1:O/l+ZT91ltMiiRJKjWLQJcGg7ypzjlb/bC5bFIRVw3M=
+github.com/nats-io/nats-server/v2 v2.10.15/go.mod h1:ul+pGt5I7e4U+nI09ZFDG4vqM+6Ce2Tou7UbVSnLiIw=
+github.com/nats-io/nats.go v1.35.0 h1:XFNqNM7v5B+MQMKqVGAyHwYhyKb48jrenXNxIU20ULk=
+github.com/nats-io/nats.go v1.35.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
 github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
 github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
 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.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
-github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
-github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
-github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
+github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g=
+github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc=
+github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
+github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
 github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
@@ -227,8 +225,8 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr
 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.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
-github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
+github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
+github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
 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=
@@ -236,20 +234,20 @@ github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p
 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.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
-github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
+github.com/prometheus/common v0.53.0 h1:U2pL9w9nmJwJDa4qqLQ3ZaePJ6ZTwt7cMD3AG3+aLCE=
+github.com/prometheus/common v0.53.0/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U=
 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.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
-github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
+github.com/prometheus/procfs v0.15.0 h1:A82kmvXJq2jTu5YUhSGNlYoxh85zLnKgPz4bMZgI5Ek=
+github.com/prometheus/procfs v0.15.0/go.mod h1:Y0RJ/Y5g5wJpkTisOtqwDSo4HwhGmLB4VQSw2sQJLHk=
 github.com/redis/go-redis/v9 v9.2.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
 github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8=
 github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
-github.com/reugn/go-quartz v0.11.2 h1:+jc54Ji06n/D/endEPmc+CuG/Jc8466nda1oxtFRrks=
-github.com/reugn/go-quartz v0.11.2/go.mod h1:no4ktgYbAAuY0E1SchR8cTx1LF4jYIzdgaQhzRPSkpk=
-github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
-github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
+github.com/reugn/go-quartz v0.12.0 h1:RsrklW++R5Swc7mCPYseXM06PTWN4N7/f1rsYkhHiww=
+github.com/reugn/go-quartz v0.12.0/go.mod h1:no4ktgYbAAuY0E1SchR8cTx1LF4jYIzdgaQhzRPSkpk=
+github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
+github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/ryanuber/columnize v2.1.2+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
@@ -276,7 +274,6 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 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.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
 github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
@@ -297,27 +294,30 @@ github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k=
-go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0 h1:dT33yIHtmsqpixFsSQPwNeY5drM9wTcoL8h0FWF4oGM=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0/go.mod h1:h95q0LBGh7hlAC08X2DhSeyIG02YQ0UyioTCVAqRPmc=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.25.0 h1:vOL89uRfOCCNIjkisd0r7SEdJF3ZJFyCNY34fdZs8eU=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.25.0/go.mod h1:8GlBGcDk8KKi7n+2S4BT/CPZQYH3erLu0/k64r1MYgo=
-go.opentelemetry.io/otel/exporters/prometheus v0.47.0 h1:OL6yk1Z/pEGdDnrBbxSsH+t4FY1zXfBRGd7bjwhlMLU=
-go.opentelemetry.io/otel/exporters/prometheus v0.47.0/go.mod h1:xF3N4OSICZDVbbYZydz9MHFro1RjmkPUKEvar2utG+Q=
-go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.25.0 h1:0vZZdECYzhTt9MKQZ5qQ0V+J3MFu4MQaQ3COfugF+FQ=
-go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.25.0/go.mod h1:e7iXx3HjaSSBXfy9ykVUlupS2Vp7LBIBuT21ousM2Hk=
-go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA=
-go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s=
-go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo=
-go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw=
-go.opentelemetry.io/otel/sdk/metric v1.25.0 h1:7CiHOy08LbrxMAp4vWpbiPcklunUshVpAvGBrdDRlGw=
-go.opentelemetry.io/otel/sdk/metric v1.25.0/go.mod h1:LzwoKptdbBBdYfvtGCzGwk6GWMA3aUzBOwtQpR6Nz7o=
-go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM=
-go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I=
-go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI=
-go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY=
+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/otel v1.26.0 h1:LQwgL5s/1W7YiiRwxf03QGnWLb2HW4pLiAhaA5cZXBs=
+go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.26.0 h1:1u/AyyOqAWzy+SkPxDpahCNZParHV8Vid1RnI2clyDE=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.26.0/go.mod h1:z46paqbJ9l7c9fIPCXTqTGwhQZ5XoTIsfeFYWboizjs=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.26.0 h1:Waw9Wfpo/IXzOI8bCB7DIk+0JZcqqsyn1JFnAc+iam8=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.26.0/go.mod h1:wnJIG4fOqyynOnnQF/eQb4/16VlX2EJAHhHgqIqWfAo=
+go.opentelemetry.io/otel/exporters/prometheus v0.48.0 h1:sBQe3VNGUjY9IKWQC6z2lNqa5iGbDSxhs60ABwK4y0s=
+go.opentelemetry.io/otel/exporters/prometheus v0.48.0/go.mod h1:DtrbMzoZWwQHyrQmCfLam5DZbnmorsGbOtTbYHycU5o=
+go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.26.0 h1:0W5o9SzoR15ocYHEQfvfipzcNog1lBxOLfnex91Hk6s=
+go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.26.0/go.mod h1:zVZ8nz+VSggWmnh6tTsJqXQ7rU4xLwRtna1M4x5jq58=
+go.opentelemetry.io/otel/metric v1.26.0 h1:7S39CLuY5Jgg9CrnA9HHiEjGMF/X2VHvoXGgSllRz30=
+go.opentelemetry.io/otel/metric v1.26.0/go.mod h1:SY+rHOI4cEawI9a7N1A4nIg/nTQXe1ccCNWYOJUrpX4=
+go.opentelemetry.io/otel/sdk v1.26.0 h1:Y7bumHf5tAiDlRYFmGqetNcLaVUZmh4iYfmGxtmz7F8=
+go.opentelemetry.io/otel/sdk v1.26.0/go.mod h1:0p8MXpqLeJ0pzcszQQN4F0S5FVjBLgypeGSngLsmirs=
+go.opentelemetry.io/otel/sdk/metric v1.26.0 h1:cWSks5tfriHPdWFnl+qpX3P681aAYqlZHcAyHw5aU9Y=
+go.opentelemetry.io/otel/sdk/metric v1.26.0/go.mod h1:ClMFFknnThJCksebJwz7KIyEDHO+nTB6gK8obLy8RyE=
+go.opentelemetry.io/otel/trace v1.26.0 h1:1ieeAUb4y0TE26jUFrCIXKpTuVK7uJGN9/Z/2LP5sQA=
+go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0=
+go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94=
+go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A=
 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=
@@ -333,16 +333,16 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
-golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
+golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
+golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
+golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
+golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 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.4.2/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.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
-golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
+golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/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=
@@ -351,21 +351,18 @@ golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
-golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
-golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
-golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
+golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
+golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
+golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=
+golang.org/x/oauth2 v0.20.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-20210220032951-036812b2e83c/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.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
 golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
 golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
@@ -383,25 +380,20 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/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-20210630005230-0f9fa26af87c/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.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
-golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
+golang.org/x/sys v0.20.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.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
-golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
+golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
+golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
 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.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
-golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
+golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
 golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -411,27 +403,20 @@ golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapK
 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
-golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
+golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
+golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
 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.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
-google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
-google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY=
-google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo=
-google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0=
-google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
-google.golang.org/grpc v1.63.0 h1:WjKe+dnvABXyPJMD7KDNLxtoGk5tgk+YFWN6cBWjZE8=
-google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
-google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
-google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+google.golang.org/genproto/googleapis/api v0.0.0-20240515191416-fc5f0ca64291 h1:4HZJ3Xv1cmrJ+0aFo304Zn79ur1HMxptAE7aCPNLSqc=
+google.golang.org/genproto/googleapis/api v0.0.0-20240515191416-fc5f0ca64291/go.mod h1:RGnPtTG7r4i8sPlNyDeikXF99hMM+hN6QMm4ooG9g2g=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 h1:AgADTJarZTBqgjiUzRgfaBchgYB3/WFTC80GPwsMcRI=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
+google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
+google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
+google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
+google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
 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=
@@ -456,15 +441,15 @@ k8s.io/apimachinery v0.29.4 h1:RaFdJiDmuKs/8cm1M6Dh1Kvyh59YQFDcFuFTSmXes6Q=
 k8s.io/apimachinery v0.29.4/go.mod h1:i3FJVwhvSp/6n8Fl4K97PJEP8C+MM+aoDq4+ZJBf70Y=
 k8s.io/client-go v0.29.4 h1:79ytIedxVfyXV8rpH3jCBW0u+un0fxHDwX5F9K8dPR8=
 k8s.io/client-go v0.29.4/go.mod h1:kC1thZQ4zQWYwldsfI088BbK6RkxK+aF5ebV8y9Q4tk=
-k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0=
-k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo=
-k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780=
-k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
+k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
+k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
+k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f h1:0LQagt0gDpKqvIkAMPaRGcXawNMouPECM1+F9BVxEaM=
+k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f/go.mod h1:S9tOR0FxgyusSNR+MboCuiDpVWkAifZvaYI1Q2ubgro=
 k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 h1:jgGTlFYnhF1PM1Ax/lAlxUPE+KfCIXHaathvJg1C3ak=
 k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0/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=
-sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
-sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
+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/hash/hasher.go b/hash/hasher.go
index e0228e0..de89003 100644
--- a/hash/hasher.go
+++ b/hash/hasher.go
@@ -25,7 +25,7 @@
 package hash
 
 import (
-	"github.com/cespare/xxhash/v2"
+	"github.com/zeebo/xxh3"
 )
 
 // Hasher defines the hashcode generator interface.
@@ -40,7 +40,7 @@ var _ Hasher = defaultHasher{}
 
 // HashCode implementation
 func (x defaultHasher) HashCode(key []byte) uint64 {
-	return xxhash.Sum64(key)
+	return xxh3.Hash(key)
 }
 
 // DefaultHasher returns the default hasher
diff --git a/internal/cluster/node.go b/internal/cluster/engine.go
similarity index 77%
rename from internal/cluster/node.go
rename to internal/cluster/engine.go
index 9455fea..a6c14b4 100644
--- a/internal/cluster/node.go
+++ b/internal/cluster/engine.go
@@ -73,9 +73,9 @@ type Event struct {
 
 // Interface defines the Node interface
 type Interface interface {
-	// Start starts the Node engine
+	// Start starts the cluster engine
 	Start(ctx context.Context) error
-	// Stop stops the Node engine
+	// Stop stops the cluster engine
 	Stop(ctx context.Context) error
 	// NodeHost returns the cluster startNode host address
 	NodeHost() string
@@ -99,14 +99,20 @@ type Interface interface {
 	// AdvertisedAddress returns the cluster node cluster address that is known by the
 	// peers in the cluster
 	AdvertisedAddress() string
+	// Peers returns a channel containing the list of peers at a given time
+	Peers(ctx context.Context) ([]*Peer, error)
 }
 
-// Node represents the Node
-type Node struct {
+// Engine represents the Engine
+type Engine struct {
 	// specifies the total number of partitions
 	// the default values is 20
 	partitionsCount uint64
 
+	// specifies the minimum number of cluster members
+	// the default values is 1
+	minimumPeersQuorum uint16
+
 	// specifies the logger
 	logger log.Logger
 
@@ -142,12 +148,12 @@ type Node struct {
 }
 
 // enforce compilation error
-var _ Interface = &Node{}
+var _ Interface = &Engine{}
 
-// NewNode creates an instance of cluster Node
-func NewNode(name string, disco discovery.Provider, host *discovery.Node, opts ...Option) (*Node, error) {
+// NewEngine creates an instance of cluster Engine
+func NewEngine(name string, disco discovery.Provider, host *discovery.Node, opts ...Option) (*Engine, error) {
 	// create an instance of the Node
-	node := &Node{
+	engine := &Engine{
 		partitionsCount:    20,
 		logger:             log.DefaultLogger,
 		name:               name,
@@ -160,30 +166,31 @@ func NewNode(name string, disco discovery.Provider, host *discovery.Node, opts .
 		events:             make(chan *Event, 20),
 		messagesReaderChan: make(chan types.Unit, 1),
 		messagesChan:       make(chan *redis.Message, 1),
+		minimumPeersQuorum: 1,
 	}
 	// apply the various options
 	for _, opt := range opts {
-		opt.Apply(node)
+		opt.Apply(engine)
 	}
 
 	// set the host startNode
-	node.host = host
+	engine.host = host
 
-	return node, nil
+	return engine, nil
 }
 
-// Start starts the Node.
-func (n *Node) Start(ctx context.Context) error {
+// Start starts the Engine.
+func (n *Engine) Start(ctx context.Context) error {
 	logger := n.logger
 
-	logger.Infof("Starting GoAkt cluster Node service on (%s)....🤔", n.host.ClusterAddress())
+	logger.Infof("Starting GoAkt cluster Engine service on host=(%s)....🤔", n.host.PeersAddress())
 
 	conf := n.buildConfig()
 	conf.Hasher = &hasherWrapper{n.hasher}
 
 	m, err := config.NewMemberlistConfig("lan")
 	if err != nil {
-		logger.Error(errors.Wrap(err, "failed to configure the cluster Node memberlist.💥"))
+		logger.Error(errors.Wrap(err, "failed to configure the cluster Engine members list.💥"))
 		return err
 	}
 
@@ -213,7 +220,7 @@ func (n *Node) Start(ctx context.Context) error {
 
 	eng, err := olric.New(conf)
 	if err != nil {
-		logger.Error(errors.Wrapf(err, "failed to start the cluster Node=(%s).💥", n.name))
+		logger.Error(errors.Wrapf(err, "failed to start the cluster Engine on host=(%s)", n.name))
 		return err
 	}
 
@@ -225,7 +232,7 @@ func (n *Node) Start(ctx context.Context) error {
 				logger.Panic(e)
 			}
 			// the expectation is to exit the application
-			logger.Fatal(errors.Wrapf(err, "failed to start the cluster Node=(%s).💥", n.name))
+			logger.Fatal(errors.Wrapf(err, "failed to start the cluster Engine on host=(%s)", n.name))
 		}
 	}()
 
@@ -236,7 +243,7 @@ func (n *Node) Start(ctx context.Context) error {
 	n.client = n.server.NewEmbeddedClient()
 	dmp, err := n.client.NewDMap(n.name)
 	if err != nil {
-		logger.Error(errors.Wrapf(err, "failed to start the cluster Node=(%s).💥", n.name))
+		logger.Error(errors.Wrapf(err, "failed to start the cluster Engine on host=(%s)", n.name))
 		return n.server.Shutdown(ctx)
 	}
 
@@ -245,7 +252,7 @@ func (n *Node) Start(ctx context.Context) error {
 	// create a subscriber to consume to cluster events
 	ps, err := n.client.NewPubSub()
 	if err != nil {
-		logger.Error(errors.Wrapf(err, "failed to start the cluster Node=(%s).💥", n.name))
+		logger.Error(errors.Wrapf(err, "failed to start the cluster Engine on host=(%s)", n.name))
 		return n.server.Shutdown(ctx)
 	}
 
@@ -253,12 +260,12 @@ func (n *Node) Start(ctx context.Context) error {
 	n.messagesChan = n.pubSub.Channel()
 	go n.consume()
 
-	logger.Infof("GoAkt cluster Node=(%s) successfully started. 🎉", n.name)
+	logger.Infof("GoAkt cluster Engine=(%s) successfully started.", n.name)
 	return nil
 }
 
-// Stop stops the Node gracefully
-func (n *Node) Stop(ctx context.Context) error {
+// Stop stops the Engine gracefully
+func (n *Engine) Stop(ctx context.Context) error {
 	// create a cancellation context of 1 second timeout
 	ctx, cancelFn := context.WithTimeout(ctx, n.shutdownTimeout)
 	defer cancelFn()
@@ -271,19 +278,19 @@ func (n *Node) Stop(ctx context.Context) error {
 
 	// close the events listener
 	if err := n.pubSub.Close(); err != nil {
-		logger.Error(errors.Wrap(err, "failed to shutdown the cluster events listener.💥"))
+		logger.Error(errors.Wrap(err, "failed to shutdown the cluster events listener"))
 		return err
 	}
 
 	// close the Node client
 	if err := n.client.Close(ctx); err != nil {
-		logger.Error(errors.Wrapf(err, "failed to shutdown the cluster Node=(%s).💥", n.name))
+		logger.Error(errors.Wrapf(err, "failed to shutdown the cluster engine=(%s)", n.name))
 		return err
 	}
 
 	// let us stop the server
 	if err := n.server.Shutdown(ctx); err != nil {
-		logger.Error(errors.Wrapf(err, "failed to Shutdown GoAkt cluster Node=(%s)....💥", n.name))
+		logger.Error(errors.Wrapf(err, "failed to shutdown the cluster engine=(%s)", n.name))
 		return err
 	}
 
@@ -293,59 +300,59 @@ func (n *Node) Stop(ctx context.Context) error {
 	// signal we are stopping listening to events
 	n.messagesReaderChan <- types.Unit{}
 
-	logger.Infof("GoAkt cluster Node=(%s) successfully stopped.🎉", n.name)
+	logger.Infof("GoAkt cluster Node=(%s) successfully stopped.", n.name)
 	return nil
 }
 
 // NodeHost returns the Node Host
-func (n *Node) NodeHost() string {
+func (n *Engine) NodeHost() string {
 	return n.host.Host
 }
 
 // NodeRemotingPort returns the Node remoting port
-func (n *Node) NodeRemotingPort() int {
+func (n *Engine) NodeRemotingPort() int {
 	return n.host.RemotingPort
 }
 
 // AdvertisedAddress returns the cluster node cluster address that is known by the
 // peers in the cluster
-func (n *Node) AdvertisedAddress() string {
-	return n.host.ClusterAddress()
+func (n *Engine) AdvertisedAddress() string {
+	return n.host.PeersAddress()
 }
 
 // PutActor replicates onto the Node the metadata of an actor
-func (n *Node) PutActor(ctx context.Context, actor *internalpb.WireActor) error {
+func (n *Engine) PutActor(ctx context.Context, actor *internalpb.WireActor) error {
 	ctx, cancelFn := context.WithTimeout(ctx, n.writeTimeout)
 	defer cancelFn()
 
 	logger := n.logger
 
-	logger.Infof("replicating actor (%s).🤔", actor.GetActorName())
+	logger.Infof("replicating actor (%s)", actor.GetActorName())
 
 	data, err := encode(actor)
 	if err != nil {
-		logger.Error(errors.Wrapf(err, "failed to persist actor=%s data in the cluster.💥", actor.GetActorName()))
+		logger.Error(errors.Wrapf(err, "failed to persist actor=%s data in the cluster", actor.GetActorName()))
 		return errors.Wrapf(err, "failed to persist actor=%s data in the cluster", actor.GetActorName())
 	}
 
 	err = n.kvStore.Put(ctx, actor.GetActorName(), data)
 	if err != nil {
-		logger.Error(errors.Wrapf(err, "failed to replicate actor=%s record.💥", actor.GetActorName()))
+		logger.Error(errors.Wrapf(err, "failed to replicate actor=%s record", actor.GetActorName()))
 		return err
 	}
 
-	logger.Infof("actor (%s) successfully replicated in the cluster.🎉", actor.GetActorName())
+	logger.Infof("actor (%s) successfully replicated in the cluster", actor.GetActorName())
 	return nil
 }
 
 // GetActor fetches an actor from the Node
-func (n *Node) GetActor(ctx context.Context, actorName string) (*internalpb.WireActor, error) {
+func (n *Engine) GetActor(ctx context.Context, actorName string) (*internalpb.WireActor, error) {
 	ctx, cancelFn := context.WithTimeout(ctx, n.readTimeout)
 	defer cancelFn()
 
 	logger := n.logger
 
-	logger.Infof("retrieving actor (%s) from the cluster.🤔", actorName)
+	logger.Infof("retrieving actor (%s) from the cluster", actorName)
 
 	resp, err := n.kvStore.Get(ctx, actorName)
 	if err != nil {
@@ -353,69 +360,69 @@ func (n *Node) GetActor(ctx context.Context, actorName string) (*internalpb.Wire
 			logger.Warnf("actor=%s is not found in the cluster", actorName)
 			return nil, ErrActorNotFound
 		}
-		logger.Error(errors.Wrapf(err, "failed to get actor=%s record.💥", actorName))
+		logger.Error(errors.Wrapf(err, "failed to get actor=%s record", actorName))
 		return nil, err
 	}
 
 	bytea, err := resp.Byte()
 	if err != nil {
-		logger.Error(errors.Wrapf(err, "failed to read the record at:{%s}.💥", actorName))
+		logger.Error(errors.Wrapf(err, "failed to read the record at:{%s}", actorName))
 		return nil, err
 	}
 
 	actor, err := decode(bytea)
 	if err != nil {
-		logger.Error(errors.Wrapf(err, "failed to decode actor=%s record.💥", actorName))
+		logger.Error(errors.Wrapf(err, "failed to decode actor=%s record", actorName))
 		return nil, err
 	}
 
-	logger.Infof("actor (%s) successfully retrieved from the cluster.🎉", actor.GetActorName())
+	logger.Infof("actor (%s) successfully retrieved from the cluster", actor.GetActorName())
 	return actor, nil
 }
 
 // RemoveActor removes a given actor from the cluster.
 // An actor is removed from the cluster when this actor has been passivated.
-func (n *Node) RemoveActor(ctx context.Context, actorName string) error {
+func (n *Engine) RemoveActor(ctx context.Context, actorName string) error {
 	logger := n.logger
 
-	logger.Infof("removing actor (%s).🤔", actorName)
+	logger.Infof("removing actor (%s)", actorName)
 
 	_, err := n.kvStore.Delete(ctx, actorName)
 	if err != nil {
-		logger.Error(errors.Wrapf(err, "failed to remove actor=%s record.💥", actorName))
+		logger.Error(errors.Wrapf(err, "failed to remove actor=%s record", actorName))
 		return err
 	}
 
-	logger.Infof("actor (%s) successfully removed from the cluster.🎉", actorName)
+	logger.Infof("actor (%s) successfully removed from the cluster", actorName)
 	return nil
 }
 
 // SetKey sets a given key to the cluster
-func (n *Node) SetKey(ctx context.Context, key string) error {
+func (n *Engine) SetKey(ctx context.Context, key string) error {
 	ctx, cancelFn := context.WithTimeout(ctx, n.writeTimeout)
 	defer cancelFn()
 
 	logger := n.logger
 
-	logger.Infof("replicating key (%s).🤔", key)
+	logger.Infof("replicating key (%s)", key)
 
 	if err := n.kvStore.Put(ctx, key, true); err != nil {
 		logger.Error(errors.Wrapf(err, "failed to replicate key=%s record.💥", key))
 		return err
 	}
 
-	logger.Infof("key (%s) successfully replicated.🎉", key)
+	logger.Infof("key (%s) successfully replicated", key)
 	return nil
 }
 
 // KeyExists checks the existence of a given key
-func (n *Node) KeyExists(ctx context.Context, key string) (bool, error) {
+func (n *Engine) KeyExists(ctx context.Context, key string) (bool, error) {
 	ctx, cancelFn := context.WithTimeout(ctx, n.readTimeout)
 	defer cancelFn()
 
 	logger := n.logger
 
-	logger.Infof("checking key (%s) existence in the cluster.🤔", key)
+	logger.Infof("checking key (%s) existence in the cluster", key)
 
 	resp, err := n.kvStore.Get(ctx, key)
 	if err != nil {
@@ -424,14 +431,14 @@ func (n *Node) KeyExists(ctx context.Context, key string) (bool, error) {
 			return false, nil
 		}
 
-		logger.Error(errors.Wrapf(err, "failed to check key=%s existence.💥", key))
+		logger.Error(errors.Wrapf(err, "failed to check key=%s existence", key))
 		return false, err
 	}
 	return resp.Bool()
 }
 
 // GetPartition returns the partition where a given actor is stored
-func (n *Node) GetPartition(actorName string) int {
+func (n *Engine) GetPartition(actorName string) int {
 	key := []byte(actorName)
 	hkey := n.hasher.HashCode(key)
 	partition := int(hkey % n.partitionsCount)
@@ -440,13 +447,31 @@ func (n *Node) GetPartition(actorName string) int {
 }
 
 // Events returns a channel where cluster events are published
-func (n *Node) Events() <-chan *Event {
+func (n *Engine) Events() <-chan *Event {
 	return n.events
 }
 
+// Peers returns a channel containing the list of peers at a given time
+func (n *Engine) Peers(ctx context.Context) ([]*Peer, error) {
+	members, err := n.client.Members(ctx)
+	if err != nil {
+		n.logger.Error(errors.Wrap(err, "failed to read cluster peers"))
+		return nil, err
+	}
+
+	peers := make([]*Peer, 0, len(members))
+	for _, member := range members {
+		if member.Name != n.AdvertisedAddress() {
+			n.logger.Debugf("node=(%s) has found peer=(%s)", n.AdvertisedAddress(), member.Name)
+			peers = append(peers, &Peer{Address: member.Name, Leader: member.Coordinator})
+		}
+	}
+	return peers, nil
+}
+
 // consume reads to the underlying cluster events
 // and emit the event
-func (n *Node) consume() {
+func (n *Engine) consume() {
 	for {
 		select {
 		case <-n.messagesReaderChan:
@@ -518,7 +543,7 @@ func (n *Node) consume() {
 }
 
 // buildConfig builds the Node configuration
-func (n *Node) buildConfig() *config.Config {
+func (n *Engine) buildConfig() *config.Config {
 	// define the log level
 	logLevel := "INFO"
 	switch n.logger.LogLevel() {
@@ -535,12 +560,12 @@ func (n *Node) buildConfig() *config.Config {
 	// create the config and return it
 	conf := &config.Config{
 		BindAddr:                   n.host.Host,
-		BindPort:                   n.host.ClusterPort,
+		BindPort:                   n.host.PeersPort,
 		ReadRepair:                 true,
-		ReplicaCount:               config.MinimumReplicaCount,
-		WriteQuorum:                config.DefaultWriteQuorum,
-		ReadQuorum:                 config.DefaultReadQuorum,
-		MemberCountQuorum:          config.DefaultMemberCountQuorum,
+		ReplicaCount:               int(n.minimumPeersQuorum),
+		WriteQuorum:                int(n.minimumPeersQuorum),
+		ReadQuorum:                 int(n.minimumPeersQuorum),
+		MemberCountQuorum:          int32(n.minimumPeersQuorum),
 		Peers:                      []string{},
 		DMaps:                      &config.DMaps{},
 		KeepAlivePeriod:            config.DefaultKeepAlivePeriod,
@@ -554,6 +579,7 @@ func (n *Node) buildConfig() *config.Config {
 		LogOutput:                  newLogWriter(n.logger),
 		EnableClusterEventsChannel: true,
 		Hasher:                     hasher.NewDefaultHasher(),
+		TriggerBalancerInterval:    config.DefaultTriggerBalancerInterval,
 	}
 
 	// set verbosity when debug is enabled
diff --git a/internal/cluster/hasher.go b/internal/cluster/hasher.go
index a9cb467..a25dc23 100644
--- a/internal/cluster/hasher.go
+++ b/internal/cluster/hasher.go
@@ -35,7 +35,7 @@ type hasherWrapper struct {
 }
 
 // enforce compilation error
-var _ hasher.Hasher = &hasherWrapper{}
+var _ hasher.Hasher = (*hasherWrapper)(nil)
 
 // Sum64 implementation
 func (x hasherWrapper) Sum64(key []byte) uint64 {
diff --git a/internal/cluster/option.go b/internal/cluster/option.go
index 8b9ba59..f1f4fe0 100644
--- a/internal/cluster/option.go
+++ b/internal/cluster/option.go
@@ -34,59 +34,66 @@ import (
 // Option is the interface that applies a configuration option.
 type Option interface {
 	// Apply sets the Option value of a config.
-	Apply(cl *Node)
+	Apply(cl *Engine)
 }
 
 var _ Option = OptionFunc(nil)
 
 // OptionFunc implements the Option interface.
-type OptionFunc func(cl *Node)
+type OptionFunc func(eng *Engine)
 
 // Apply applies the Node's option
-func (f OptionFunc) Apply(c *Node) {
+func (f OptionFunc) Apply(c *Engine) {
 	f(c)
 }
 
 // WithPartitionsCount sets the total number of partitions
 func WithPartitionsCount(count uint64) Option {
-	return OptionFunc(func(cl *Node) {
-		cl.partitionsCount = count
+	return OptionFunc(func(eng *Engine) {
+		eng.partitionsCount = count
 	})
 }
 
 // WithLogger sets the logger
 func WithLogger(logger log.Logger) Option {
-	return OptionFunc(func(cl *Node) {
-		cl.logger = logger
+	return OptionFunc(func(eng *Engine) {
+		eng.logger = logger
 	})
 }
 
 // WithWriteTimeout sets the Node write timeout.
 // This timeout specifies the timeout of a data replication
 func WithWriteTimeout(timeout time.Duration) Option {
-	return OptionFunc(func(cl *Node) {
-		cl.writeTimeout = timeout
+	return OptionFunc(func(eng *Engine) {
+		eng.writeTimeout = timeout
 	})
 }
 
 // WithReadTimeout sets the Node read timeout.
 // This timeout specifies the timeout of a data retrieval
 func WithReadTimeout(timeout time.Duration) Option {
-	return OptionFunc(func(cl *Node) {
-		cl.readTimeout = timeout
+	return OptionFunc(func(eng *Engine) {
+		eng.readTimeout = timeout
 	})
 }
 
 // WithShutdownTimeout sets the Node shutdown timeout.
 func WithShutdownTimeout(timeout time.Duration) Option {
-	return OptionFunc(func(cl *Node) {
-		cl.shutdownTimeout = timeout
+	return OptionFunc(func(eng *Engine) {
+		eng.shutdownTimeout = timeout
 	})
 }
 
 // WithHasher sets the custom hasher
 func WithHasher(hasher hash.Hasher) Option {
-	return OptionFunc(func(cl *Node) {
-		cl.hasher = hasher
+	return OptionFunc(func(eng *Engine) {
+		eng.hasher = hasher
+	})
+}
+
+// WithMinimumPeersQuorum sets the minimum number of nodes to form a quorum
+func WithMinimumPeersQuorum(count uint16) Option {
+	return OptionFunc(func(eng *Engine) {
+		eng.minimumPeersQuorum = count
 	})
 }
diff --git a/internal/cluster/peer.go b/internal/cluster/peer.go
new file mode 100644
index 0000000..8473f80
--- /dev/null
+++ b/internal/cluster/peer.go
@@ -0,0 +1,35 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022-2024 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
+ * 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 cluster
+
+// Peer defines the peer info
+type Peer struct {
+	// Address represents the peer address. It is also the host:port of the peer
+	Address string
+	// Leader states that the given peer is the leader not.
+	// A peer is a leader when it is the oldest node in the cluster
+	Leader bool
+}
diff --git a/internal/types/registry.go b/internal/types/registry.go
index 9acb85a..5d0ba23 100644
--- a/internal/types/registry.go
+++ b/internal/types/registry.go
@@ -27,11 +27,9 @@ package types
 import (
 	"reflect"
 	"strings"
-	"sync"
-)
 
-// Map defines mapping between a type name and its runtime type
-type Map map[string]reflect.Type
+	"github.com/alphadose/haxmap"
+)
 
 // Registry defines the types registry interface
 type Registry interface {
@@ -42,7 +40,7 @@ type Registry interface {
 	// Exists return true when a given object is in the registry
 	Exists(v any) bool
 	// TypesMap returns the list of registered at any point in time
-	TypesMap() Map
+	TypesMap() map[string]reflect.Type
 	// Type returns the type of object
 	Type(v any) (reflect.Type, bool)
 	// TypeOf returns the type of an object name
@@ -50,8 +48,7 @@ type Registry interface {
 }
 
 type registry struct {
-	typesMap Map
-	mu       *sync.RWMutex
+	typesMap *haxmap.Map[string, reflect.Type]
 }
 
 var _ Registry = (*registry)(nil)
@@ -59,58 +56,53 @@ var _ Registry = (*registry)(nil)
 // NewRegistry creates a new types registry
 func NewRegistry() Registry {
 	return &registry{
-		typesMap: Map{},
-		mu:       new(sync.RWMutex),
+		// TODO need to check with memory footprint here since we change the map engine
+		typesMap: haxmap.New[string, reflect.Type](100),
 	}
 }
 
 // Deregister removes the registered object from the registry
 func (r *registry) Deregister(v any) {
-	r.mu.Lock()
 	name, _ := nameAndTypeOf(v)
-	delete(r.typesMap, name)
-	r.mu.Unlock()
+	r.typesMap.Del(name)
 }
 
 // Exists return true when a given object is in the registry
 func (r *registry) Exists(v any) bool {
-	r.mu.RLock()
 	name, _ := nameAndTypeOf(v)
-	_, ok := r.typesMap[name]
-	r.mu.RUnlock()
+	_, ok := r.typesMap.Get(name)
 	return ok
 }
 
 // TypesMap returns the list of registered at any point in time
-func (r *registry) TypesMap() Map {
-	r.mu.Lock()
-	out := r.typesMap
-	r.mu.Unlock()
+func (r *registry) TypesMap() map[string]reflect.Type {
+	out := make(map[string]reflect.Type, r.typesMap.Len())
+	r.typesMap.ForEach(func(s string, t reflect.Type) bool {
+		if len(out) == int(r.typesMap.Len()) {
+			return false
+		}
+		out[s] = t
+		return true
+	})
 	return out
 }
 
 // Register an object
 func (r *registry) Register(v any) {
-	r.mu.Lock()
 	name, rtype := nameAndTypeOf(v)
-	r.typesMap[name] = rtype
-	r.mu.Unlock()
+	r.typesMap.Set(name, rtype)
 }
 
 // Type returns the type of object
 func (r *registry) Type(v any) (reflect.Type, bool) {
-	r.mu.RLock()
 	name, _ := nameAndTypeOf(v)
-	out, ok := r.typesMap[name]
-	r.mu.RUnlock()
+	out, ok := r.typesMap.Get(name)
 	return out, ok
 }
 
 // TypeOf returns the type of an object name
 func (r *registry) TypeOf(name string) (reflect.Type, bool) {
-	r.mu.RLock()
-	out, ok := r.typesMap[strings.ToLower(name)]
-	r.mu.RUnlock()
+	out, ok := r.typesMap.Get(strings.ToLower(name))
 	return out, ok
 }
 
diff --git a/mocks/cluster/interface.go b/mocks/cluster/interface.go
index 99ab3cc..e1c2d38 100644
--- a/mocks/cluster/interface.go
+++ b/mocks/cluster/interface.go
@@ -340,6 +340,60 @@ func (_c *Interface_NodeRemotingPort_Call) RunAndReturn(run func() int) *Interfa
 	return _c
 }
 
+// Peers provides a mock function with given fields: ctx
+func (_m *Interface) Peers(ctx context.Context) ([]*internalcluster.Peer, error) {
+	ret := _m.Called(ctx)
+
+	var r0 []*internalcluster.Peer
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context) ([]*internalcluster.Peer, error)); ok {
+		return rf(ctx)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context) []*internalcluster.Peer); ok {
+		r0 = rf(ctx)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]*internalcluster.Peer)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context) error); ok {
+		r1 = rf(ctx)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Interface_Peers_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Peers'
+type Interface_Peers_Call struct {
+	*mock.Call
+}
+
+// Peers is a helper method to define mock.On call
+//   - ctx context.Context
+func (_e *Interface_Expecter) Peers(ctx interface{}) *Interface_Peers_Call {
+	return &Interface_Peers_Call{Call: _e.mock.On("Peers", ctx)}
+}
+
+func (_c *Interface_Peers_Call) Run(run func(ctx context.Context)) *Interface_Peers_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(context.Context))
+	})
+	return _c
+}
+
+func (_c *Interface_Peers_Call) Return(_a0 []*internalcluster.Peer, _a1 error) *Interface_Peers_Call {
+	_c.Call.Return(_a0, _a1)
+	return _c
+}
+
+func (_c *Interface_Peers_Call) RunAndReturn(run func(context.Context) ([]*internalcluster.Peer, error)) *Interface_Peers_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
 // PutActor provides a mock function with given fields: ctx, actor
 func (_m *Interface) PutActor(ctx context.Context, actor *internalpb.WireActor) error {
 	ret := _m.Called(ctx, actor)
diff --git a/mocks/cluster/option.go b/mocks/cluster/option.go
index cedc8e3..18b8e36 100644
--- a/mocks/cluster/option.go
+++ b/mocks/cluster/option.go
@@ -21,7 +21,7 @@ func (_m *Option) EXPECT() *Option_Expecter {
 }
 
 // Apply provides a mock function with given fields: cl
-func (_m *Option) Apply(cl *internalcluster.Node) {
+func (_m *Option) Apply(cl *internalcluster.Engine) {
 	_m.Called(cl)
 }
 
@@ -31,14 +31,14 @@ type Option_Apply_Call struct {
 }
 
 // Apply is a helper method to define mock.On call
-//   - cl *internalcluster.Node
+//   - cl *internalcluster.Engine
 func (_e *Option_Expecter) Apply(cl interface{}) *Option_Apply_Call {
 	return &Option_Apply_Call{Call: _e.mock.On("Apply", cl)}
 }
 
-func (_c *Option_Apply_Call) Run(run func(cl *internalcluster.Node)) *Option_Apply_Call {
+func (_c *Option_Apply_Call) Run(run func(cl *internalcluster.Engine)) *Option_Apply_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(*internalcluster.Node))
+		run(args[0].(*internalcluster.Engine))
 	})
 	return _c
 }
@@ -48,7 +48,7 @@ func (_c *Option_Apply_Call) Return() *Option_Apply_Call {
 	return _c
 }
 
-func (_c *Option_Apply_Call) RunAndReturn(run func(*internalcluster.Node)) *Option_Apply_Call {
+func (_c *Option_Apply_Call) RunAndReturn(run func(*internalcluster.Engine)) *Option_Apply_Call {
 	_c.Call.Return(run)
 	return _c
 }
diff --git a/mocks/cluster/option_func.go b/mocks/cluster/option_func.go
index c8ba57a..a028f2b 100644
--- a/mocks/cluster/option_func.go
+++ b/mocks/cluster/option_func.go
@@ -20,9 +20,9 @@ func (_m *OptionFunc) EXPECT() *OptionFunc_Expecter {
 	return &OptionFunc_Expecter{mock: &_m.Mock}
 }
 
-// Execute provides a mock function with given fields: cl
-func (_m *OptionFunc) Execute(cl *internalcluster.Node) {
-	_m.Called(cl)
+// Execute provides a mock function with given fields: eng
+func (_m *OptionFunc) Execute(eng *internalcluster.Engine) {
+	_m.Called(eng)
 }
 
 // OptionFunc_Execute_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Execute'
@@ -31,14 +31,14 @@ type OptionFunc_Execute_Call struct {
 }
 
 // Execute is a helper method to define mock.On call
-//   - cl *internalcluster.Node
-func (_e *OptionFunc_Expecter) Execute(cl interface{}) *OptionFunc_Execute_Call {
-	return &OptionFunc_Execute_Call{Call: _e.mock.On("Execute", cl)}
+//   - eng *internalcluster.Engine
+func (_e *OptionFunc_Expecter) Execute(eng interface{}) *OptionFunc_Execute_Call {
+	return &OptionFunc_Execute_Call{Call: _e.mock.On("Execute", eng)}
 }
 
-func (_c *OptionFunc_Execute_Call) Run(run func(cl *internalcluster.Node)) *OptionFunc_Execute_Call {
+func (_c *OptionFunc_Execute_Call) Run(run func(eng *internalcluster.Engine)) *OptionFunc_Execute_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(*internalcluster.Node))
+		run(args[0].(*internalcluster.Engine))
 	})
 	return _c
 }
@@ -48,7 +48,7 @@ func (_c *OptionFunc_Execute_Call) Return() *OptionFunc_Execute_Call {
 	return _c
 }
 
-func (_c *OptionFunc_Execute_Call) RunAndReturn(run func(*internalcluster.Node)) *OptionFunc_Execute_Call {
+func (_c *OptionFunc_Execute_Call) RunAndReturn(run func(*internalcluster.Engine)) *OptionFunc_Execute_Call {
 	_c.Call.Return(run)
 	return _c
 }
diff --git a/protos/internal/remoting.proto b/protos/internal/remoting.proto
index 8354d70..d4e161e 100644
--- a/protos/internal/remoting.proto
+++ b/protos/internal/remoting.proto
@@ -11,6 +11,8 @@ option go_package = "github.com/tochemey/goakt/internal/internalpb;internalpb";
 // With remoting one can:
 // - send messages to actors
 // - lookup actor remotely
+// - spawn actor remotely
+// - stop actor remotely
 service RemotingService {
   // RemoteAsk is used to send a message to an actor remotely and expect a response immediately.
   rpc RemoteAsk(stream RemoteAskRequest) returns (stream RemoteAskResponse);


Test patch:
diff --git a/actors/actor_system_test.go b/actors/actor_system_test.go
index feecd71..8f186f0 100644
--- a/actors/actor_system_test.go
+++ b/actors/actor_system_test.go
@@ -182,7 +182,7 @@ func TestActorSystem(t *testing.T) {
 			WithLogger(logger),
 			WithReplyTimeout(time.Minute),
 			WithRemoting(host, int32(remotingPort)),
-			WithClustering(provider, 9, gossipPort, clusterPort))
+			WithClustering(provider, 9, 1, gossipPort, clusterPort))
 		require.NoError(t, err)
 
 		provider.EXPECT().ID().Return("testDisco")
@@ -947,7 +947,7 @@ func TestActorSystem(t *testing.T) {
 			WithLogger(logger),
 			WithReplyTimeout(time.Minute),
 			WithRemoting(host, int32(remotingPort)),
-			WithClustering(provider, 9, gossipPort, clusterPort))
+			WithClustering(provider, 9, 1, gossipPort, clusterPort))
 		require.NoError(t, err)
 
 		provider.EXPECT().ID().Return("testDisco")
@@ -1172,7 +1172,7 @@ func TestActorSystem(t *testing.T) {
 			WithLogger(logger),
 			WithReplyTimeout(time.Minute),
 			WithRemoting(host, int32(remotingPort)),
-			WithClustering(provider, 9, gossipPort, clusterPort))
+			WithClustering(provider, 9, 1, gossipPort, clusterPort))
 		require.NoError(t, err)
 
 		provider.EXPECT().ID().Return("testDisco")
diff --git a/actors/actor_test.go b/actors/actor_test.go
index 252f205..1d1362a 100644
--- a/actors/actor_test.go
+++ b/actors/actor_test.go
@@ -444,7 +444,7 @@ func startClusterSystem(t *testing.T, nodeName, serverAddr string) (ActorSystem,
 		Name:         host,
 		Host:         host,
 		GossipPort:   gossipPort,
-		ClusterPort:  clusterPort,
+		PeersPort:    clusterPort,
 		RemotingPort: remotingPort,
 	}
 
@@ -458,7 +458,7 @@ func startClusterSystem(t *testing.T, nodeName, serverAddr string) (ActorSystem,
 		WithLogger(logger),
 		WithReplyTimeout(time.Minute),
 		WithRemoting(host, int32(remotingPort)),
-		WithClustering(provider, 10, gossipPort, clusterPort))
+		WithClustering(provider, 10, 1, gossipPort, clusterPort))
 
 	require.NotNil(t, system)
 	require.NoError(t, err)
diff --git a/actors/scheduler_test.go b/actors/scheduler_test.go
index 601869e..1275ee7 100644
--- a/actors/scheduler_test.go
+++ b/actors/scheduler_test.go
@@ -73,7 +73,8 @@ func TestScheduler(t *testing.T) {
 
 		time.Sleep(time.Second)
 		typedSystem := newActorSystem.(*actorSystem)
-		keys := typedSystem.scheduler.quartzScheduler.GetJobKeys()
+		keys, err := typedSystem.scheduler.quartzScheduler.GetJobKeys()
+		require.NoError(t, err)
 		assert.Empty(t, keys)
 		assert.EqualValues(t, 1, actor.counter.Load())
 		assert.EqualValues(t, 1, actor.counter.Load())
@@ -152,7 +153,8 @@ func TestScheduler(t *testing.T) {
 		time.Sleep(time.Second)
 		typedSystem := newActorSystem.(*actorSystem)
 		// for test purpose only
-		keys := typedSystem.scheduler.quartzScheduler.GetJobKeys()
+		keys, err := typedSystem.scheduler.quartzScheduler.GetJobKeys()
+		require.NoError(t, err)
 		assert.Empty(t, keys)
 		assert.EqualValues(t, 1, actor.counter.Load())
 		assert.EqualValues(t, 1, actor.counter.Load())
diff --git a/discovery/kubernetes/config_test.go b/discovery/kubernetes/config_test.go
index 5683ff9..d156672 100644
--- a/discovery/kubernetes/config_test.go
+++ b/discovery/kubernetes/config_test.go
@@ -37,7 +37,7 @@ func TestConfig(t *testing.T) {
 			ApplicationName:  "applicationName",
 			GossipPortName:   "gossipName",
 			RemotingPortName: "remotingName",
-			ClusterPortName:  "clusterPortName",
+			PeersPortName:    "peersPortName",
 			ActorSystemName:  "actorSys",
 		}
 		assert.NoError(t, config.Validate())
@@ -48,7 +48,7 @@ func TestConfig(t *testing.T) {
 			ApplicationName:  "applicationName",
 			GossipPortName:   "",
 			RemotingPortName: "remotingName",
-			ClusterPortName:  "clusterPortName",
+			PeersPortName:    "peersPortName",
 			ActorSystemName:  "actorSys",
 		}
 		assert.Error(t, config.Validate())
diff --git a/discovery/kubernetes/discovery_test.go b/discovery/kubernetes/discovery_test.go
index 93fd1f3..53e37b4 100644
--- a/discovery/kubernetes/discovery_test.go
+++ b/discovery/kubernetes/discovery_test.go
@@ -41,7 +41,7 @@ import (
 
 const (
 	gossipPortName   = "gossip-port"
-	clusterPortName  = "cluster-port"
+	peersPortName    = "peers-port"
 	remotingPortName = "remoting-port"
 )
 
@@ -79,7 +79,7 @@ func TestDiscovery(t *testing.T) {
 			ApplicationName:  "test",
 			GossipPortName:   gossipPortName,
 			RemotingPortName: remotingPortName,
-			ClusterPortName:  clusterPortName,
+			PeersPortName:    peersPortName,
 		}
 
 		// create some bunch of mock pods
@@ -103,7 +103,7 @@ func TestDiscovery(t *testing.T) {
 									ContainerPort: 3379,
 								},
 								{
-									Name:          clusterPortName,
+									Name:          peersPortName,
 									ContainerPort: 3380,
 								},
 								{
@@ -147,7 +147,7 @@ func TestDiscovery(t *testing.T) {
 									ContainerPort: 3379,
 								},
 								{
-									Name:          clusterPortName,
+									Name:          peersPortName,
 									ContainerPort: 3380,
 								},
 								{
@@ -215,7 +215,7 @@ func TestDiscovery(t *testing.T) {
 			ApplicationName:  applicationName,
 			GossipPortName:   gossipPortName,
 			RemotingPortName: remotingPortName,
-			ClusterPortName:  clusterPortName,
+			PeersPortName:    peersPortName,
 		}
 
 		// create the instance of provider
diff --git a/discovery/nats/discovery_test.go b/discovery/nats/discovery_test.go
index 78aa40c..cb137d0 100644
--- a/discovery/nats/discovery_test.go
+++ b/discovery/nats/discovery_test.go
@@ -87,7 +87,7 @@ func newPeer(t *testing.T, serverAddr string) *Discovery {
 		Name:         host,
 		Host:         host,
 		GossipPort:   gossipPort,
-		ClusterPort:  clusterPort,
+		PeersPort:    clusterPort,
 		RemotingPort: remotingPort,
 	}
 
@@ -131,7 +131,7 @@ func TestDiscovery(t *testing.T) {
 			Name:         host,
 			Host:         host,
 			GossipPort:   gossipPort,
-			ClusterPort:  clusterPort,
+			PeersPort:    clusterPort,
 			RemotingPort: remotingPort,
 		}
 
@@ -175,7 +175,7 @@ func TestDiscovery(t *testing.T) {
 			Name:         host,
 			Host:         host,
 			GossipPort:   gossipPort,
-			ClusterPort:  clusterPort,
+			PeersPort:    clusterPort,
 			RemotingPort: remotingPort,
 		}
 		// create the instance of provider
@@ -215,7 +215,7 @@ func TestDiscovery(t *testing.T) {
 			Name:         host,
 			Host:         host,
 			GossipPort:   gossipPort,
-			ClusterPort:  clusterPort,
+			PeersPort:    clusterPort,
 			RemotingPort: remotingPort,
 		}
 
@@ -260,7 +260,7 @@ func TestDiscovery(t *testing.T) {
 			Name:         host,
 			Host:         host,
 			GossipPort:   gossipPort,
-			ClusterPort:  clusterPort,
+			PeersPort:    clusterPort,
 			RemotingPort: remotingPort,
 		}
 
@@ -301,7 +301,7 @@ func TestDiscovery(t *testing.T) {
 			Name:         host,
 			Host:         host,
 			GossipPort:   gossipPort,
-			ClusterPort:  clusterPort,
+			PeersPort:    clusterPort,
 			RemotingPort: remotingPort,
 		}
 
@@ -343,7 +343,7 @@ func TestDiscovery(t *testing.T) {
 			Name:         host,
 			Host:         host,
 			GossipPort:   gossipPort,
-			ClusterPort:  clusterPort,
+			PeersPort:    clusterPort,
 			RemotingPort: remotingPort,
 		}
 
@@ -444,7 +444,7 @@ func TestDiscovery(t *testing.T) {
 			Name:         host,
 			Host:         host,
 			GossipPort:   gossipPort,
-			ClusterPort:  clusterPort,
+			PeersPort:    clusterPort,
 			RemotingPort: remotingPort,
 		}
 
diff --git a/internal/cluster/node_test.go b/internal/cluster/engine_test.go
similarity index 92%
rename from internal/cluster/node_test.go
rename to internal/cluster/engine_test.go
index 01676a7..578fa27 100644
--- a/internal/cluster/node_test.go
+++ b/internal/cluster/engine_test.go
@@ -79,12 +79,12 @@ func TestSingleNode(t *testing.T) {
 			Name:         host,
 			Host:         host,
 			GossipPort:   gossipPort,
-			ClusterPort:  clusterPort,
+			PeersPort:    clusterPort,
 			RemotingPort: remotingPort,
 		}
 
 		logger := log.New(log.ErrorLevel, os.Stdout)
-		cluster, err := NewNode("test", provider, &hostNode, WithLogger(logger))
+		cluster, err := NewEngine("test", provider, &hostNode, WithLogger(logger))
 		require.NotNil(t, cluster)
 		require.NoError(t, err)
 
@@ -134,11 +134,11 @@ func TestSingleNode(t *testing.T) {
 			Name:         host,
 			Host:         host,
 			GossipPort:   gossipPort,
-			ClusterPort:  clusterPort,
+			PeersPort:    clusterPort,
 			RemotingPort: remotingPort,
 		}
 
-		cluster, err := NewNode("test", provider, &hostNode)
+		cluster, err := NewEngine("test", provider, &hostNode)
 		require.NotNil(t, cluster)
 		require.NoError(t, err)
 
@@ -204,11 +204,11 @@ func TestSingleNode(t *testing.T) {
 			Name:         host,
 			Host:         host,
 			GossipPort:   gossipPort,
-			ClusterPort:  clusterPort,
+			PeersPort:    clusterPort,
 			RemotingPort: remotingPort,
 		}
 
-		cluster, err := NewNode("test", provider, &hostNode)
+		cluster, err := NewEngine("test", provider, &hostNode)
 		require.NotNil(t, cluster)
 		require.NoError(t, err)
 
@@ -267,12 +267,12 @@ func TestSingleNode(t *testing.T) {
 			Name:         host,
 			Host:         host,
 			GossipPort:   gossipPort,
-			ClusterPort:  clusterPort,
+			PeersPort:    clusterPort,
 			RemotingPort: remotingPort,
 		}
 
 		logger := log.New(log.WarningLevel, os.Stdout)
-		cluster, err := NewNode("test", provider, &hostNode, WithLogger(logger))
+		cluster, err := NewEngine("test", provider, &hostNode, WithLogger(logger))
 		require.NotNil(t, cluster)
 		require.NoError(t, err)
 
@@ -314,14 +314,14 @@ func TestMultipleNodes(t *testing.T) {
 	srv := startNatsServer(t)
 
 	// create a cluster node1
-	node1, sd1 := startNode(t, "node1", srv.Addr().String())
+	node1, sd1 := startEngine(t, "node1", srv.Addr().String())
 	require.NotNil(t, node1)
 
 	// wait for the node to start properly
 	time.Sleep(2 * time.Second)
 
 	// create a cluster node1
-	node2, sd2 := startNode(t, "node2", srv.Addr().String())
+	node2, sd2 := startEngine(t, "node2", srv.Addr().String())
 	require.NotNil(t, node2)
 	node2Addr := node2.AdvertisedAddress()
 
@@ -353,6 +353,10 @@ L:
 	require.True(t, ok)
 	require.NotNil(t, nodeJoined)
 	require.Equal(t, node2Addr, nodeJoined.GetAddress())
+	peers, err := node1.Peers(context.TODO())
+	require.NoError(t, err)
+	require.Len(t, peers, 1)
+	require.Equal(t, node2Addr, peers[0].Address)
 
 	// wait for some time
 	time.Sleep(time.Second)
@@ -419,7 +423,7 @@ func startNatsServer(t *testing.T) *natsserver.Server {
 	return serv
 }
 
-func startNode(t *testing.T, nodeName, serverAddr string) (*Node, discovery.Provider) {
+func startEngine(t *testing.T, nodeName, serverAddr string) (*Engine, discovery.Provider) {
 	// create a context
 	ctx := context.TODO()
 
@@ -448,7 +452,7 @@ func startNode(t *testing.T, nodeName, serverAddr string) (*Node, discovery.Prov
 		Name:         host,
 		Host:         host,
 		GossipPort:   gossipPort,
-		ClusterPort:  clusterPort,
+		PeersPort:    clusterPort,
 		RemotingPort: remotingPort,
 	}
 
@@ -456,13 +460,13 @@ func startNode(t *testing.T, nodeName, serverAddr string) (*Node, discovery.Prov
 	provider := nats.NewDiscovery(&config, &hostNode)
 
 	// create the startNode
-	node, err := NewNode(nodeName, provider, &hostNode)
+	engine, err := NewEngine(nodeName, provider, &hostNode)
 	require.NoError(t, err)
-	require.NotNil(t, node)
+	require.NotNil(t, engine)
 
 	// start the node
-	require.NoError(t, node.Start(ctx))
+	require.NoError(t, engine.Start(ctx))
 
 	// return the cluster startNode
-	return node, provider
+	return engine, provider
 }
diff --git a/internal/cluster/option_test.go b/internal/cluster/option_test.go
index 9283708..cae1569 100644
--- a/internal/cluster/option_test.go
+++ b/internal/cluster/option_test.go
@@ -39,43 +39,49 @@ func TestOptions(t *testing.T) {
 	testCases := []struct {
 		name     string
 		option   Option
-		expected Node
+		expected Engine
 	}{
 		{
 			name:     "WithPartitionsCount",
 			option:   WithPartitionsCount(2),
-			expected: Node{partitionsCount: 2},
+			expected: Engine{partitionsCount: 2},
 		},
 		{
 			name:     "WithLogger",
 			option:   WithLogger(log.DefaultLogger),
-			expected: Node{logger: log.DefaultLogger},
+			expected: Engine{logger: log.DefaultLogger},
 		},
 		{
 			name:     "WithWriteTimeout",
 			option:   WithWriteTimeout(2 * time.Minute),
-			expected: Node{writeTimeout: 2 * time.Minute},
+			expected: Engine{writeTimeout: 2 * time.Minute},
 		},
 		{
 			name:     "WithReadTimeout",
 			option:   WithReadTimeout(2 * time.Minute),
-			expected: Node{readTimeout: 2 * time.Minute},
+			expected: Engine{readTimeout: 2 * time.Minute},
 		},
 		{
 			name:     "WithShutdownTimeout",
 			option:   WithShutdownTimeout(2 * time.Minute),
-			expected: Node{shutdownTimeout: 2 * time.Minute},
+			expected: Engine{shutdownTimeout: 2 * time.Minute},
 		},
 		{
 			name:     "WithHasher",
 			option:   WithHasher(mockHasher),
-			expected: Node{hasher: mockHasher},
+			expected: Engine{hasher: mockHasher},
+		},
+
+		{
+			name:     "WithMinimumNodes",
+			option:   WithMinimumPeersQuorum(3),
+			expected: Engine{minimumPeersQuorum: 3},
 		},
 	}
 
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
-			var cl Node
+			var cl Engine
 			tc.option.Apply(&cl)
 			assert.Equal(t, tc.expected, cl)
 		})