From 3f9af6d952e80e67b37ed92dde29893212f083a8 Mon Sep 17 00:00:00 2001
From: Brian Conway <bconway@rcesoftware.com>
Date: Thu, 21 Dec 2023 08:24:07 -0600
Subject: [PATCH] Update gomock to v0.4.0

- Remove linter workaround.
- Sync `vendor/` for updated dependencies.
- All test variations pass.
---
 go.mod                                        |  10 +-
 go.sum                                        |  20 ++--
 .../hermes-api/api/mock_respwriter_test.go    |   1 +
 .../hermes-api/service/mock_apper_test.go     |   1 +
 .../hermes-api/service/mock_eventer_test.go   |   1 +
 .../service/mock_identityer_test.go           |   1 +
 .../hermes-api/service/mock_keyer_test.go     |   1 +
 internal/hermes-api/service/mock_orger.go     |   1 +
 .../hermes-api/service/mock_userer_test.go    |   1 +
 .../notifier/mock_apper_test.go               |   1 +
 pkg/cache/mock_cacher.go                      |   1 +
 pkg/notify/mock_notifier.go                   |   1 +
 pkg/queue/mock_queuer.go                      |   1 +
 pkg/test/matcher/mock_protoer_test.go         |   1 +
 pkg/test/matcher/mock_recenter_test.go        |   1 +
 .../jellydator/ttlcache/v3/cache.go           |   7 ++
 vendor/github.com/redis/go-redis/v9/README.md |  11 +-
 .../redis/go-redis/v9/bitmap_commands.go      |  22 +++-
 .../github.com/redis/go-redis/v9/command.go   | 102 ++++++++++++++++--
 .../github.com/redis/go-redis/v9/commands.go  |  18 +++-
 .../go-redis/v9/internal/hscan/structmap.go   |   6 +-
 vendor/github.com/redis/go-redis/v9/json.go   |   9 +-
 .../redis/go-redis/v9/osscluster.go           |  15 +--
 .../github.com/redis/go-redis/v9/package.json |   2 +-
 vendor/github.com/redis/go-redis/v9/redis.go  |  13 +++
 vendor/github.com/redis/go-redis/v9/ring.go   |  18 +---
 .../redis/go-redis/v9/sortedset_commands.go   |   2 +-
 .../redis/go-redis/v9/timeseries_commands.go  |   2 +
 .../github.com/redis/go-redis/v9/version.go   |   2 +-
 vendor/go.uber.org/mock/CONTRIBUTORS          |  37 -------
 vendor/go.uber.org/mock/gomock/controller.go  |   2 +
 vendor/go.uber.org/mock/gomock/matchers.go    |  75 +++++++++++++
 vendor/google.golang.org/grpc/server.go       |  22 ++--
 vendor/google.golang.org/grpc/version.go      |   2 +-
 vendor/modules.txt                            |  10 +-
 35 files changed, 305 insertions(+), 115 deletions(-)
 delete mode 100644 vendor/go.uber.org/mock/CONTRIBUTORS

diff --git a/go.mod b/go.mod
index b88b4777..9d6cd771 100644
--- a/go.mod
+++ b/go.mod
@@ -8,17 +8,17 @@ require (
 	github.com/gregdel/pushover v1.3.0
 	github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1
 	github.com/jackc/pgx/v5 v5.5.1
-	github.com/jellydator/ttlcache/v3 v3.1.0
+	github.com/jellydator/ttlcache/v3 v3.1.1
 	github.com/mennanov/fmutils v0.2.1
 	github.com/nsqio/go-nsq v1.1.0
 	github.com/ownmfa/proto/go v1.1.0
-	github.com/redis/go-redis/v9 v9.3.0
+	github.com/redis/go-redis/v9 v9.3.1
 	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
 	github.com/smira/go-statsd v1.3.3
 	github.com/stretchr/testify v1.8.4
-	go.uber.org/mock v0.3.0
-	golang.org/x/crypto v0.16.0
-	google.golang.org/grpc v1.60.0
+	go.uber.org/mock v0.4.0
+	golang.org/x/crypto v0.17.0
+	google.golang.org/grpc v1.60.1
 	google.golang.org/protobuf v1.31.0
 )
 
diff --git a/go.sum b/go.sum
index e404126d..02dc8796 100644
--- a/go.sum
+++ b/go.sum
@@ -536,8 +536,8 @@ github.com/jackc/pgx/v5 v5.5.1 h1:5I9etrGkLrN+2XPCsi6XLlV5DITbSL/xBZdmAxFcXPI=
 github.com/jackc/pgx/v5 v5.5.1/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA=
 github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
 github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
-github.com/jellydator/ttlcache/v3 v3.1.0 h1:0gPFG0IHHP6xyUyXq+JaD8fwkDCqgqwohXNJBcYE71g=
-github.com/jellydator/ttlcache/v3 v3.1.0/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVKySzCVW6+0gA2n4=
+github.com/jellydator/ttlcache/v3 v3.1.1 h1:RCgYJqo3jgvhl+fEWvjNW8thxGWsgxi+TPhRir1Y9y8=
+github.com/jellydator/ttlcache/v3 v3.1.1/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVKySzCVW6+0gA2n4=
 github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
@@ -557,8 +557,8 @@ github.com/ownmfa/proto/go v1.1.0/go.mod h1:5CxxrnBJr06Agy5TSA2jX/2q/fB9qcXZ03tL
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0=
-github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
+github.com/redis/go-redis/v9 v9.3.1 h1:KqdY8U+3X6z+iACvumCNxnoluToB+9Me+TvyFa21Mds=
+github.com/redis/go-redis/v9 v9.3.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
 github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
@@ -598,16 +598,16 @@ go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
 go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
 go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
 go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
-go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
-go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
+go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
+go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
-golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
+golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
+golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1087,8 +1087,8 @@ google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCD
 google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
 google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
 google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
-google.golang.org/grpc v1.60.0 h1:6FQAR0kM31P6MRdeluor2w2gPaS4SVNrD/DNTxrQ15k=
-google.golang.org/grpc v1.60.0/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
+google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
+google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
diff --git a/internal/hermes-api/api/mock_respwriter_test.go b/internal/hermes-api/api/mock_respwriter_test.go
index a26609c0..a2e2eccb 100644
--- a/internal/hermes-api/api/mock_respwriter_test.go
+++ b/internal/hermes-api/api/mock_respwriter_test.go
@@ -5,6 +5,7 @@
 //
 //	mockgen -destination mock_respwriter_test.go -package api -build_flags=-mod=mod net/http ResponseWriter
 //
+
 // Package api is a generated GoMock package.
 package api
 
diff --git a/internal/hermes-api/service/mock_apper_test.go b/internal/hermes-api/service/mock_apper_test.go
index 2e0f23c0..5c856233 100644
--- a/internal/hermes-api/service/mock_apper_test.go
+++ b/internal/hermes-api/service/mock_apper_test.go
@@ -5,6 +5,7 @@
 //
 //	mockgen -source app.go -destination mock_apper_test.go -package service
 //
+
 // Package service is a generated GoMock package.
 package service
 
diff --git a/internal/hermes-api/service/mock_eventer_test.go b/internal/hermes-api/service/mock_eventer_test.go
index fcef365a..ab418614 100644
--- a/internal/hermes-api/service/mock_eventer_test.go
+++ b/internal/hermes-api/service/mock_eventer_test.go
@@ -5,6 +5,7 @@
 //
 //	mockgen -source event.go -destination mock_eventer_test.go -package service
 //
+
 // Package service is a generated GoMock package.
 package service
 
diff --git a/internal/hermes-api/service/mock_identityer_test.go b/internal/hermes-api/service/mock_identityer_test.go
index 051f6062..4fda111c 100644
--- a/internal/hermes-api/service/mock_identityer_test.go
+++ b/internal/hermes-api/service/mock_identityer_test.go
@@ -5,6 +5,7 @@
 //
 //	mockgen -source identity.go -destination mock_identityer_test.go -package service
 //
+
 // Package service is a generated GoMock package.
 package service
 
diff --git a/internal/hermes-api/service/mock_keyer_test.go b/internal/hermes-api/service/mock_keyer_test.go
index 3b661d86..53a81ae4 100644
--- a/internal/hermes-api/service/mock_keyer_test.go
+++ b/internal/hermes-api/service/mock_keyer_test.go
@@ -5,6 +5,7 @@
 //
 //	mockgen -source session.go -destination mock_keyer_test.go -package service
 //
+
 // Package service is a generated GoMock package.
 package service
 
diff --git a/internal/hermes-api/service/mock_orger.go b/internal/hermes-api/service/mock_orger.go
index 52bef1df..9e40816a 100644
--- a/internal/hermes-api/service/mock_orger.go
+++ b/internal/hermes-api/service/mock_orger.go
@@ -5,6 +5,7 @@
 //
 //	mockgen -source org.go -destination mock_orger.go -package service
 //
+
 // Package service is a generated GoMock package.
 package service
 
diff --git a/internal/hermes-api/service/mock_userer_test.go b/internal/hermes-api/service/mock_userer_test.go
index 227c74d9..a92a8765 100644
--- a/internal/hermes-api/service/mock_userer_test.go
+++ b/internal/hermes-api/service/mock_userer_test.go
@@ -5,6 +5,7 @@
 //
 //	mockgen -source user.go -destination mock_userer_test.go -package service
 //
+
 // Package service is a generated GoMock package.
 package service
 
diff --git a/internal/hermes-notifier/notifier/mock_apper_test.go b/internal/hermes-notifier/notifier/mock_apper_test.go
index 3ff5e9ff..c0dc7dcd 100644
--- a/internal/hermes-notifier/notifier/mock_apper_test.go
+++ b/internal/hermes-notifier/notifier/mock_apper_test.go
@@ -5,6 +5,7 @@
 //
 //	mockgen -source notifier.go -destination mock_apper_test.go -package notifier
 //
+
 // Package notifier is a generated GoMock package.
 package notifier
 
diff --git a/pkg/cache/mock_cacher.go b/pkg/cache/mock_cacher.go
index 301223fe..abe65498 100644
--- a/pkg/cache/mock_cacher.go
+++ b/pkg/cache/mock_cacher.go
@@ -5,6 +5,7 @@
 //
 //	mockgen -source cacher.go -destination mock_cacher.go -package cache
 //
+
 // Package cache is a generated GoMock package.
 package cache
 
diff --git a/pkg/notify/mock_notifier.go b/pkg/notify/mock_notifier.go
index c08637ba..38bae90b 100644
--- a/pkg/notify/mock_notifier.go
+++ b/pkg/notify/mock_notifier.go
@@ -5,6 +5,7 @@
 //
 //	mockgen -source notifier.go -destination mock_notifier.go -package notify
 //
+
 // Package notify is a generated GoMock package.
 package notify
 
diff --git a/pkg/queue/mock_queuer.go b/pkg/queue/mock_queuer.go
index 72326597..310463a4 100644
--- a/pkg/queue/mock_queuer.go
+++ b/pkg/queue/mock_queuer.go
@@ -5,6 +5,7 @@
 //
 //	mockgen -source queuer.go -destination mock_queuer.go -package queue
 //
+
 // Package queue is a generated GoMock package.
 package queue
 
diff --git a/pkg/test/matcher/mock_protoer_test.go b/pkg/test/matcher/mock_protoer_test.go
index ff8f41f0..aa5d4fe6 100644
--- a/pkg/test/matcher/mock_protoer_test.go
+++ b/pkg/test/matcher/mock_protoer_test.go
@@ -5,6 +5,7 @@
 //
 //	mockgen -source proto_test.go -destination mock_protoer_test.go -package matcher
 //
+
 // Package matcher is a generated GoMock package.
 package matcher
 
diff --git a/pkg/test/matcher/mock_recenter_test.go b/pkg/test/matcher/mock_recenter_test.go
index a1e75f66..0c830394 100644
--- a/pkg/test/matcher/mock_recenter_test.go
+++ b/pkg/test/matcher/mock_recenter_test.go
@@ -5,6 +5,7 @@
 //
 //	mockgen -source recent_test.go -destination mock_recenter_test.go -package matcher
 //
+
 // Package matcher is a generated GoMock package.
 package matcher
 
diff --git a/vendor/github.com/jellydator/ttlcache/v3/cache.go b/vendor/github.com/jellydator/ttlcache/v3/cache.go
index 93943da2..a1547fcb 100644
--- a/vendor/github.com/jellydator/ttlcache/v3/cache.go
+++ b/vendor/github.com/jellydator/ttlcache/v3/cache.go
@@ -478,6 +478,13 @@ func (c *Cache[K, V]) Items() map[K]*Item[K, V] {
 // Range stops the iteration.
 func (c *Cache[K, V]) Range(fn func(item *Item[K, V]) bool) {
 	c.items.mu.RLock()
+
+	// Check if cache is empty
+	if c.items.lru.Len() == 0 {
+		c.items.mu.RUnlock()
+		return
+	}
+
 	for item := c.items.lru.Front(); item != c.items.lru.Back().Next(); item = item.Next() {
 		i := item.Value.(*Item[K, V])
 		c.items.mu.RUnlock()
diff --git a/vendor/github.com/redis/go-redis/v9/README.md b/vendor/github.com/redis/go-redis/v9/README.md
index 8965b915..98edbe21 100644
--- a/vendor/github.com/redis/go-redis/v9/README.md
+++ b/vendor/github.com/redis/go-redis/v9/README.md
@@ -149,15 +149,16 @@ import (
     "github.com/redis/go-redis/v9"
 )
 
-var ctx = context.Background()
-
-func ExampleClient() {
-    url := "redis://localhost:6379?password=hello&protocol=3"
+func ExampleClient() *redis.Client {
+    url := "redis://user:password@localhost:6379/0?protocol=3"
     opts, err := redis.ParseURL(url)
     if err != nil {
         panic(err)
     }
-    rdb := redis.NewClient(opts)
+
+    return redis.NewClient(opts)
+}
+
 ```
 
 ## Contributing
diff --git a/vendor/github.com/redis/go-redis/v9/bitmap_commands.go b/vendor/github.com/redis/go-redis/v9/bitmap_commands.go
index 1436b02a..550d7f52 100644
--- a/vendor/github.com/redis/go-redis/v9/bitmap_commands.go
+++ b/vendor/github.com/redis/go-redis/v9/bitmap_commands.go
@@ -1,6 +1,8 @@
 package redis
 
-import "context"
+import (
+	"context"
+)
 
 type BitMapCmdable interface {
 	GetBit(ctx context.Context, key string, offset int64) *IntCmd
@@ -127,3 +129,21 @@ func (c cmdable) BitField(ctx context.Context, key string, values ...interface{}
 	_ = c(ctx, cmd)
 	return cmd
 }
+
+// BitFieldRO - Read-only variant of the BITFIELD command.
+// It is like the original BITFIELD but only accepts GET subcommand and can safely be used in read-only replicas.
+// - BitFieldRO(ctx, key, "<Encoding0>", "<Offset0>", "<Encoding1>","<Offset1>")
+func (c cmdable) BitFieldRO(ctx context.Context, key string, values ...interface{}) *IntSliceCmd {
+	args := make([]interface{}, 2, 2+len(values))
+	args[0] = "BITFIELD_RO"
+	args[1] = key
+	if len(values)%2 != 0 {
+		panic("BitFieldRO: invalid number of arguments, must be even")
+	}
+	for i := 0; i < len(values); i += 2 {
+		args = append(args, "GET", values[i], values[i+1])
+	}
+	cmd := NewIntSliceCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
diff --git a/vendor/github.com/redis/go-redis/v9/command.go b/vendor/github.com/redis/go-redis/v9/command.go
index c641a3fa..4ef335a9 100644
--- a/vendor/github.com/redis/go-redis/v9/command.go
+++ b/vendor/github.com/redis/go-redis/v9/command.go
@@ -8,6 +8,7 @@ import (
 	"regexp"
 	"strconv"
 	"strings"
+	"sync"
 	"time"
 
 	"github.com/redis/go-redis/v9/internal"
@@ -17,10 +18,22 @@ import (
 )
 
 type Cmder interface {
+	// command name.
+	// e.g. "set k v ex 10" -> "set", "cluster info" -> "cluster".
 	Name() string
+
+	// full command name.
+	// e.g. "set k v ex 10" -> "set", "cluster info" -> "cluster info".
 	FullName() string
+
+	// all args of the command.
+	// e.g. "set k v ex 10" -> "[set k v ex 10]".
 	Args() []interface{}
+
+	// format request and response string.
+	// e.g. "set k v ex 10" -> "set k v ex 10: OK", "get k" -> "get k: v".
 	String() string
+
 	stringArg(int) string
 	firstKeyPos() int8
 	SetFirstKeyPos(int8)
@@ -62,7 +75,7 @@ func writeCmd(wr *proto.Writer, cmd Cmder) error {
 	return wr.WriteArgs(cmd.Args())
 }
 
-func cmdFirstKeyPos(cmd Cmder, info *CommandInfo) int {
+func cmdFirstKeyPos(cmd Cmder) int {
 	if pos := cmd.firstKeyPos(); pos != 0 {
 		return int(pos)
 	}
@@ -82,10 +95,6 @@ func cmdFirstKeyPos(cmd Cmder, info *CommandInfo) int {
 			return 2
 		}
 	}
-
-	if info != nil {
-		return int(info.FirstKeyPos)
-	}
 	return 1
 }
 
@@ -5369,7 +5378,6 @@ func (cmd *InfoCmd) readReply(rd *proto.Reader) error {
 	}
 
 	return nil
-
 }
 
 func (cmd *InfoCmd) Item(section, key string) string {
@@ -5381,3 +5389,85 @@ func (cmd *InfoCmd) Item(section, key string) string {
 		return cmd.val[section][key]
 	}
 }
+
+type MonitorStatus int
+
+const (
+	monitorStatusIdle MonitorStatus = iota
+	monitorStatusStart
+	monitorStatusStop
+)
+
+type MonitorCmd struct {
+	baseCmd
+	ch     chan string
+	status MonitorStatus
+	mu     sync.Mutex
+}
+
+func newMonitorCmd(ctx context.Context, ch chan string) *MonitorCmd {
+	return &MonitorCmd{
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: []interface{}{"monitor"},
+		},
+		ch:     ch,
+		status: monitorStatusIdle,
+		mu:     sync.Mutex{},
+	}
+}
+
+func (cmd *MonitorCmd) String() string {
+	return cmdString(cmd, nil)
+}
+
+func (cmd *MonitorCmd) readReply(rd *proto.Reader) error {
+	ctx, cancel := context.WithCancel(cmd.ctx)
+	go func(ctx context.Context) {
+		for {
+			select {
+			case <-ctx.Done():
+				return
+			default:
+				err := cmd.readMonitor(rd, cancel)
+				if err != nil {
+					cmd.err = err
+					return
+				}
+			}
+		}
+	}(ctx)
+	return nil
+}
+
+func (cmd *MonitorCmd) readMonitor(rd *proto.Reader, cancel context.CancelFunc) error {
+	for {
+		cmd.mu.Lock()
+		st := cmd.status
+		cmd.mu.Unlock()
+		if pk, _ := rd.Peek(1); len(pk) != 0 && st == monitorStatusStart {
+			line, err := rd.ReadString()
+			if err != nil {
+				return err
+			}
+			cmd.ch <- line
+		}
+		if st == monitorStatusStop {
+			cancel()
+			break
+		}
+	}
+	return nil
+}
+
+func (cmd *MonitorCmd) Start() {
+	cmd.mu.Lock()
+	defer cmd.mu.Unlock()
+	cmd.status = monitorStatusStart
+}
+
+func (cmd *MonitorCmd) Stop() {
+	cmd.mu.Lock()
+	defer cmd.mu.Unlock()
+	cmd.status = monitorStatusStop
+}
diff --git a/vendor/github.com/redis/go-redis/v9/commands.go b/vendor/github.com/redis/go-redis/v9/commands.go
index 842cc23a..546ebafb 100644
--- a/vendor/github.com/redis/go-redis/v9/commands.go
+++ b/vendor/github.com/redis/go-redis/v9/commands.go
@@ -204,7 +204,6 @@ type Cmdable interface {
 	SlowLogGet(ctx context.Context, num int64) *SlowLogCmd
 	Time(ctx context.Context) *TimeCmd
 	DebugObject(ctx context.Context, key string) *StringCmd
-
 	MemoryUsage(ctx context.Context, key string, samples ...int) *IntCmd
 
 	ModuleLoadex(ctx context.Context, conf *ModuleLoadexConfig) *StringCmd
@@ -700,3 +699,20 @@ func (c cmdable) ModuleLoadex(ctx context.Context, conf *ModuleLoadexConfig) *St
 	_ = c(ctx, cmd)
 	return cmd
 }
+
+/*
+Monitor - represents a Redis MONITOR command, allowing the user to capture
+and process all commands sent to a Redis server. This mimics the behavior of
+MONITOR in the redis-cli.
+
+Notes:
+- Using MONITOR blocks the connection to the server for itself. It needs a dedicated connection
+- The user should create a channel of type string
+- This runs concurrently in the background. Trigger via the Start and Stop functions
+See further: Redis MONITOR command: https://redis.io/commands/monitor
+*/
+func (c cmdable) Monitor(ctx context.Context, ch chan string) *MonitorCmd {
+	cmd := newMonitorCmd(ctx, ch)
+	_ = c(ctx, cmd)
+	return cmd
+}
diff --git a/vendor/github.com/redis/go-redis/v9/internal/hscan/structmap.go b/vendor/github.com/redis/go-redis/v9/internal/hscan/structmap.go
index 234079f0..1a560e4a 100644
--- a/vendor/github.com/redis/go-redis/v9/internal/hscan/structmap.go
+++ b/vendor/github.com/redis/go-redis/v9/internal/hscan/structmap.go
@@ -61,7 +61,11 @@ func newStructSpec(t reflect.Type, fieldTag string) *structSpec {
 		}
 
 		// Use the built-in decoder.
-		out.set(tag, &structField{index: i, fn: decoders[f.Type.Kind()]})
+		kind := f.Type.Kind()
+		if kind == reflect.Pointer {
+			kind = f.Type.Elem().Kind()
+		}
+		out.set(tag, &structField{index: i, fn: decoders[kind]})
 	}
 
 	return out
diff --git a/vendor/github.com/redis/go-redis/v9/json.go b/vendor/github.com/redis/go-redis/v9/json.go
index f9425241..f0d3abbd 100644
--- a/vendor/github.com/redis/go-redis/v9/json.go
+++ b/vendor/github.com/redis/go-redis/v9/json.go
@@ -66,7 +66,6 @@ type JSONCmd struct {
 var _ Cmder = (*JSONCmd)(nil)
 
 func newJSONCmd(ctx context.Context, args ...interface{}) *JSONCmd {
-
 	return &JSONCmd{
 		baseCmd: baseCmd{
 			ctx:  ctx,
@@ -95,7 +94,6 @@ func (cmd *JSONCmd) Val() string {
 	} else {
 		return cmd.val
 	}
-
 }
 
 func (cmd *JSONCmd) Result() (string, error) {
@@ -103,7 +101,6 @@ func (cmd *JSONCmd) Result() (string, error) {
 }
 
 func (cmd JSONCmd) Expanded() (interface{}, error) {
-
 	if len(cmd.val) != 0 && cmd.expanded == nil {
 		err := json.Unmarshal([]byte(cmd.val), &cmd.expanded)
 		if err != nil {
@@ -115,7 +112,6 @@ func (cmd JSONCmd) Expanded() (interface{}, error) {
 }
 
 func (cmd *JSONCmd) readReply(rd *proto.Reader) error {
-
 	// nil response from JSON.(M)GET (cmd.baseCmd.err will be "redis: nil")
 	if cmd.baseCmd.Err() == Nil {
 		cmd.val = ""
@@ -131,7 +127,7 @@ func (cmd *JSONCmd) readReply(rd *proto.Reader) error {
 			return err
 		}
 
-		var expanded = make([]interface{}, size)
+		expanded := make([]interface{}, size)
 
 		for i := 0; i < size; i++ {
 			if expanded[i], err = rd.ReadReply(); err != nil {
@@ -186,7 +182,6 @@ func (cmd *JSONSliceCmd) Result() ([]interface{}, error) {
 }
 
 func (cmd *JSONSliceCmd) readReply(rd *proto.Reader) error {
-
 	if cmd.baseCmd.Err() == Nil {
 		cmd.val = nil
 		return Nil
@@ -220,7 +215,6 @@ func (cmd *JSONSliceCmd) readReply(rd *proto.Reader) error {
 		}
 	}
 	return nil
-
 }
 
 /*******************************************************************************
@@ -262,7 +256,6 @@ func (cmd *IntPointerSliceCmd) Result() ([]*int64, error) {
 }
 
 func (cmd *IntPointerSliceCmd) readReply(rd *proto.Reader) error {
-
 	n, err := rd.ReadArrayLen()
 	if err != nil {
 		return err
diff --git a/vendor/github.com/redis/go-redis/v9/osscluster.go b/vendor/github.com/redis/go-redis/v9/osscluster.go
index 93e0eef1..b8a82d9b 100644
--- a/vendor/github.com/redis/go-redis/v9/osscluster.go
+++ b/vendor/github.com/redis/go-redis/v9/osscluster.go
@@ -907,7 +907,6 @@ func (c *ClusterClient) Process(ctx context.Context, cmd Cmder) error {
 }
 
 func (c *ClusterClient) process(ctx context.Context, cmd Cmder) error {
-	cmdInfo := c.cmdInfo(ctx, cmd.Name())
 	slot := c.cmdSlot(ctx, cmd)
 	var node *clusterNode
 	var ask bool
@@ -921,7 +920,7 @@ func (c *ClusterClient) process(ctx context.Context, cmd Cmder) error {
 
 		if node == nil {
 			var err error
-			node, err = c.cmdNode(ctx, cmdInfo, slot)
+			node, err = c.cmdNode(ctx, cmd.Name(), slot)
 			if err != nil {
 				return err
 			}
@@ -1783,8 +1782,7 @@ func (c *ClusterClient) cmdSlot(ctx context.Context, cmd Cmder) int {
 		return args[2].(int)
 	}
 
-	cmdInfo := c.cmdInfo(ctx, cmd.Name())
-	return cmdSlot(cmd, cmdFirstKeyPos(cmd, cmdInfo))
+	return cmdSlot(cmd, cmdFirstKeyPos(cmd))
 }
 
 func cmdSlot(cmd Cmder, pos int) int {
@@ -1797,7 +1795,7 @@ func cmdSlot(cmd Cmder, pos int) int {
 
 func (c *ClusterClient) cmdNode(
 	ctx context.Context,
-	cmdInfo *CommandInfo,
+	cmdName string,
 	slot int,
 ) (*clusterNode, error) {
 	state, err := c.state.Get(ctx)
@@ -1805,8 +1803,11 @@ func (c *ClusterClient) cmdNode(
 		return nil, err
 	}
 
-	if c.opt.ReadOnly && cmdInfo != nil && cmdInfo.ReadOnly {
-		return c.slotReadOnlyNode(state, slot)
+	if c.opt.ReadOnly {
+		cmdInfo := c.cmdInfo(ctx, cmdName)
+		if cmdInfo != nil && cmdInfo.ReadOnly {
+			return c.slotReadOnlyNode(state, slot)
+		}
 	}
 	return state.slotMasterNode(slot)
 }
diff --git a/vendor/github.com/redis/go-redis/v9/package.json b/vendor/github.com/redis/go-redis/v9/package.json
index 73063efd..cfba3581 100644
--- a/vendor/github.com/redis/go-redis/v9/package.json
+++ b/vendor/github.com/redis/go-redis/v9/package.json
@@ -1,6 +1,6 @@
 {
   "name": "redis",
-  "version": "9.3.0",
+  "version": "9.3.1",
   "main": "index.js",
   "repository": "git@github.com:redis/go-redis.git",
   "author": "Vladimir Mihailenco <vladimir.webdev@gmail.com>",
diff --git a/vendor/github.com/redis/go-redis/v9/redis.go b/vendor/github.com/redis/go-redis/v9/redis.go
index 9430eb75..9792af76 100644
--- a/vendor/github.com/redis/go-redis/v9/redis.go
+++ b/vendor/github.com/redis/go-redis/v9/redis.go
@@ -5,6 +5,7 @@ import (
 	"errors"
 	"fmt"
 	"net"
+	"sync"
 	"sync/atomic"
 	"time"
 
@@ -40,12 +41,15 @@ type (
 )
 
 type hooksMixin struct {
+	hooksMu *sync.Mutex
+
 	slice   []Hook
 	initial hooks
 	current hooks
 }
 
 func (hs *hooksMixin) initHooks(hooks hooks) {
+	hs.hooksMu = new(sync.Mutex)
 	hs.initial = hooks
 	hs.chain()
 }
@@ -116,6 +120,9 @@ func (hs *hooksMixin) AddHook(hook Hook) {
 func (hs *hooksMixin) chain() {
 	hs.initial.setDefaults()
 
+	hs.hooksMu.Lock()
+	defer hs.hooksMu.Unlock()
+
 	hs.current.dial = hs.initial.dial
 	hs.current.process = hs.initial.process
 	hs.current.pipeline = hs.initial.pipeline
@@ -138,9 +145,13 @@ func (hs *hooksMixin) chain() {
 }
 
 func (hs *hooksMixin) clone() hooksMixin {
+	hs.hooksMu.Lock()
+	defer hs.hooksMu.Unlock()
+
 	clone := *hs
 	l := len(clone.slice)
 	clone.slice = clone.slice[:l:l]
+	clone.hooksMu = new(sync.Mutex)
 	return clone
 }
 
@@ -165,6 +176,8 @@ func (hs *hooksMixin) withProcessPipelineHook(
 }
 
 func (hs *hooksMixin) dialHook(ctx context.Context, network, addr string) (net.Conn, error) {
+	hs.hooksMu.Lock()
+	defer hs.hooksMu.Unlock()
 	return hs.current.dial(ctx, network, addr)
 }
 
diff --git a/vendor/github.com/redis/go-redis/v9/ring.go b/vendor/github.com/redis/go-redis/v9/ring.go
index 367a542e..0c031b5a 100644
--- a/vendor/github.com/redis/go-redis/v9/ring.go
+++ b/vendor/github.com/redis/go-redis/v9/ring.go
@@ -678,21 +678,8 @@ func (c *Ring) cmdsInfo(ctx context.Context) (map[string]*CommandInfo, error) {
 	return nil, firstErr
 }
 
-func (c *Ring) cmdInfo(ctx context.Context, name string) *CommandInfo {
-	cmdsInfo, err := c.cmdsInfoCache.Get(ctx)
-	if err != nil {
-		return nil
-	}
-	info := cmdsInfo[name]
-	if info == nil {
-		internal.Logger.Printf(ctx, "info for cmd=%s not found", name)
-	}
-	return info
-}
-
 func (c *Ring) cmdShard(ctx context.Context, cmd Cmder) (*ringShard, error) {
-	cmdInfo := c.cmdInfo(ctx, cmd.Name())
-	pos := cmdFirstKeyPos(cmd, cmdInfo)
+	pos := cmdFirstKeyPos(cmd)
 	if pos == 0 {
 		return c.sharding.Random()
 	}
@@ -760,8 +747,7 @@ func (c *Ring) generalProcessPipeline(
 	cmdsMap := make(map[string][]Cmder)
 
 	for _, cmd := range cmds {
-		cmdInfo := c.cmdInfo(ctx, cmd.Name())
-		hash := cmd.stringArg(cmdFirstKeyPos(cmd, cmdInfo))
+		hash := cmd.stringArg(cmdFirstKeyPos(cmd))
 		if hash != "" {
 			hash = c.sharding.Hash(hash)
 		}
diff --git a/vendor/github.com/redis/go-redis/v9/sortedset_commands.go b/vendor/github.com/redis/go-redis/v9/sortedset_commands.go
index 67014027..7e96e1eb 100644
--- a/vendor/github.com/redis/go-redis/v9/sortedset_commands.go
+++ b/vendor/github.com/redis/go-redis/v9/sortedset_commands.go
@@ -727,7 +727,7 @@ func (c cmdable) ZScan(ctx context.Context, key string, cursor uint64, match str
 // Z represents sorted set member.
 type Z struct {
 	Score  float64
-	Member interface{}
+	Member string
 }
 
 // ZWithKey represents sorted set member including the name of the key where it was popped.
diff --git a/vendor/github.com/redis/go-redis/v9/timeseries_commands.go b/vendor/github.com/redis/go-redis/v9/timeseries_commands.go
index 61cc3a5b..6f1b2fa4 100644
--- a/vendor/github.com/redis/go-redis/v9/timeseries_commands.go
+++ b/vendor/github.com/redis/go-redis/v9/timeseries_commands.go
@@ -531,6 +531,8 @@ func (c cmdable) TSInfoWithArgs(ctx context.Context, key string, options *TSInfo
 }
 
 // TSMAdd - Adds multiple samples to multiple time-series keys.
+// It accepts a slice of 'ktv' slices, each containing exactly three elements: key, timestamp, and value.
+// This struct must be provided for this command to work.
 // For more information - https://redis.io/commands/ts.madd/
 func (c cmdable) TSMAdd(ctx context.Context, ktvSlices [][]interface{}) *IntSliceCmd {
 	args := []interface{}{"TS.MADD"}
diff --git a/vendor/github.com/redis/go-redis/v9/version.go b/vendor/github.com/redis/go-redis/v9/version.go
index b9ca0641..a6c6905b 100644
--- a/vendor/github.com/redis/go-redis/v9/version.go
+++ b/vendor/github.com/redis/go-redis/v9/version.go
@@ -2,5 +2,5 @@ package redis
 
 // Version is the current release version.
 func Version() string {
-	return "9.3.0"
+	return "9.3.1"
 }
diff --git a/vendor/go.uber.org/mock/CONTRIBUTORS b/vendor/go.uber.org/mock/CONTRIBUTORS
deleted file mode 100644
index def849ca..00000000
--- a/vendor/go.uber.org/mock/CONTRIBUTORS
+++ /dev/null
@@ -1,37 +0,0 @@
-# This is the official list of people who can contribute (and typically
-# have contributed) code to the gomock repository.
-# The AUTHORS file lists the copyright holders; this file
-# lists people.  For example, Google employees are listed here
-# but not in AUTHORS, because Google holds the copyright.
-#
-# The submission process automatically checks to make sure
-# that people submitting code are listed in this file (by email address).
-#
-# Names should be added to this file only after verifying that
-# the individual or the individual's organization has agreed to
-# the appropriate Contributor License Agreement, found here:
-#
-#     http://code.google.com/legal/individual-cla-v1.0.html
-#     http://code.google.com/legal/corporate-cla-v1.0.html
-#
-# The agreement for individuals can be filled out on the web.
-#
-# When adding J Random Contributor's name to this file,
-# either J's name or J's organization's name should be
-# added to the AUTHORS file, depending on whether the
-# individual or corporate CLA was used.
-
-# Names should be added to this file like so:
-#     Name <email address>
-#
-# An entry with two email addresses specifies that the
-# first address should be used in the submit logs and
-# that the second address should be recognized as the
-# same person when interacting with Rietveld.
-
-# Please keep the list sorted.
-
-Aaron Jacobs <jacobsa@google.com> <aaronjjacobs@gmail.com>
-Alex Reece <awreece@gmail.com>
-David Symonds <dsymonds@golang.org>
-Ryan Barrett <ryanb@google.com>
diff --git a/vendor/go.uber.org/mock/gomock/controller.go b/vendor/go.uber.org/mock/gomock/controller.go
index 6846d0d0..9d17a2f0 100644
--- a/vendor/go.uber.org/mock/gomock/controller.go
+++ b/vendor/go.uber.org/mock/gomock/controller.go
@@ -246,6 +246,8 @@ func (ctrl *Controller) Finish() {
 // Satisfied returns whether all expected calls bound to this Controller have been satisfied.
 // Calling Finish is then guaranteed to not fail due to missing calls.
 func (ctrl *Controller) Satisfied() bool {
+	ctrl.mu.Lock()
+	defer ctrl.mu.Unlock()
 	return ctrl.expectedCalls.Satisfied()
 }
 
diff --git a/vendor/go.uber.org/mock/gomock/matchers.go b/vendor/go.uber.org/mock/gomock/matchers.go
index bac4623b..c1725509 100644
--- a/vendor/go.uber.org/mock/gomock/matchers.go
+++ b/vendor/go.uber.org/mock/gomock/matchers.go
@@ -17,6 +17,7 @@ package gomock
 import (
 	"fmt"
 	"reflect"
+	"regexp"
 	"strings"
 )
 
@@ -168,6 +169,25 @@ func (n notMatcher) String() string {
 	return "not(" + n.m.String() + ")"
 }
 
+type regexMatcher struct {
+	regex *regexp.Regexp
+}
+
+func (m regexMatcher) Matches(x any) bool {
+	switch t := x.(type) {
+	case string:
+		return m.regex.MatchString(t)
+	case []byte:
+		return m.regex.Match(t)
+	default:
+		return false
+	}
+}
+
+func (m regexMatcher) String() string {
+	return "matches regex " + m.regex.String()
+}
+
 type assignableToTypeOfMatcher struct {
 	targetType reflect.Type
 }
@@ -180,6 +200,27 @@ func (m assignableToTypeOfMatcher) String() string {
 	return "is assignable to " + m.targetType.Name()
 }
 
+type anyOfMatcher struct {
+	matchers []Matcher
+}
+
+func (am anyOfMatcher) Matches(x any) bool {
+	for _, m := range am.matchers {
+		if m.Matches(x) {
+			return true
+		}
+	}
+	return false
+}
+
+func (am anyOfMatcher) String() string {
+	ss := make([]string, 0, len(am.matchers))
+	for _, matcher := range am.matchers {
+		ss = append(ss, matcher.String())
+	}
+	return strings.Join(ss, " | ")
+}
+
 type allMatcher struct {
 	matchers []Matcher
 }
@@ -302,6 +343,28 @@ func Any() Matcher { return anyMatcher{} }
 //	Cond(func(x any){return x.(int) == 2}).Matches(1) // returns false
 func Cond(fn func(x any) bool) Matcher { return condMatcher{fn} }
 
+// AnyOf returns a composite Matcher that returns true if at least one of the
+// matchers returns true.
+//
+// Example usage:
+//
+//	AnyOf(1, 2, 3).Matches(2) // returns true
+//	AnyOf(1, 2, 3).Matches(10) // returns false
+//	AnyOf(Nil(), Len(2)).Matches(nil) // returns true
+//	AnyOf(Nil(), Len(2)).Matches("hi") // returns true
+//	AnyOf(Nil(), Len(2)).Matches("hello") // returns false
+func AnyOf(xs ...any) Matcher {
+	ms := make([]Matcher, 0, len(xs))
+	for _, x := range xs {
+		if m, ok := x.(Matcher); ok {
+			ms = append(ms, m)
+		} else {
+			ms = append(ms, Eq(x))
+		}
+	}
+	return anyOfMatcher{ms}
+}
+
 // Eq returns a matcher that matches on equality.
 //
 // Example usage:
@@ -339,6 +402,18 @@ func Not(x any) Matcher {
 	return notMatcher{Eq(x)}
 }
 
+// Regex checks whether parameter matches the associated regex.
+//
+// Example usage:
+//
+//	Regex("[0-9]{2}:[0-9]{2}").Matches("23:02") // returns true
+//	Regex("[0-9]{2}:[0-9]{2}").Matches([]byte{'2', '3', ':', '0', '2'}) // returns true
+//	Regex("[0-9]{2}:[0-9]{2}").Matches("hello world") // returns false
+//	Regex("[0-9]{2}").Matches(21) // returns false as it's not a valid type
+func Regex(regexStr string) Matcher {
+	return regexMatcher{regex: regexp.MustCompile(regexStr)}
+}
+
 // AssignableToTypeOf is a Matcher that matches if the parameter to the mock
 // function is assignable to the type of the parameter to this function.
 //
diff --git a/vendor/google.golang.org/grpc/server.go b/vendor/google.golang.org/grpc/server.go
index 2fa694d5..682fa183 100644
--- a/vendor/google.golang.org/grpc/server.go
+++ b/vendor/google.golang.org/grpc/server.go
@@ -144,7 +144,8 @@ type Server struct {
 	channelzID *channelz.Identifier
 	czData     *channelzData
 
-	serverWorkerChannel chan func()
+	serverWorkerChannel      chan func()
+	serverWorkerChannelClose func()
 }
 
 type serverOptions struct {
@@ -623,15 +624,14 @@ func (s *Server) serverWorker() {
 // connections to reduce the time spent overall on runtime.morestack.
 func (s *Server) initServerWorkers() {
 	s.serverWorkerChannel = make(chan func())
+	s.serverWorkerChannelClose = grpcsync.OnceFunc(func() {
+		close(s.serverWorkerChannel)
+	})
 	for i := uint32(0); i < s.opts.numServerWorkers; i++ {
 		go s.serverWorker()
 	}
 }
 
-func (s *Server) stopServerWorkers() {
-	close(s.serverWorkerChannel)
-}
-
 // NewServer creates a gRPC server which has no service registered and has not
 // started to accept requests yet.
 func NewServer(opt ...ServerOption) *Server {
@@ -1898,15 +1898,19 @@ func (s *Server) stop(graceful bool) {
 		s.closeServerTransportsLocked()
 	}
 
-	if s.opts.numServerWorkers > 0 {
-		s.stopServerWorkers()
-	}
-
 	for len(s.conns) != 0 {
 		s.cv.Wait()
 	}
 	s.conns = nil
 
+	if s.opts.numServerWorkers > 0 {
+		// Closing the channel (only once, via grpcsync.OnceFunc) after all the
+		// connections have been closed above ensures that there are no
+		// goroutines executing the callback passed to st.HandleStreams (where
+		// the channel is written to).
+		s.serverWorkerChannelClose()
+	}
+
 	if s.events != nil {
 		s.events.Finish()
 		s.events = nil
diff --git a/vendor/google.golang.org/grpc/version.go b/vendor/google.golang.org/grpc/version.go
index a04793ae..dc2cea59 100644
--- a/vendor/google.golang.org/grpc/version.go
+++ b/vendor/google.golang.org/grpc/version.go
@@ -19,4 +19,4 @@
 package grpc
 
 // Version is the current grpc version.
-const Version = "1.60.0"
+const Version = "1.60.1"
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 0a6e9b41..383b01e3 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -61,7 +61,7 @@ github.com/jackc/pgx/v5/stdlib
 ## explicit; go 1.19
 github.com/jackc/puddle/v2
 github.com/jackc/puddle/v2/internal/genstack
-# github.com/jellydator/ttlcache/v3 v3.1.0
+# github.com/jellydator/ttlcache/v3 v3.1.1
 ## explicit; go 1.18
 github.com/jellydator/ttlcache/v3
 # github.com/mennanov/fmutils v0.2.1
@@ -76,7 +76,7 @@ github.com/ownmfa/proto/go/api
 # github.com/pmezard/go-difflib v1.0.0
 ## explicit
 github.com/pmezard/go-difflib/difflib
-# github.com/redis/go-redis/v9 v9.3.0
+# github.com/redis/go-redis/v9 v9.3.1
 ## explicit; go 1.18
 github.com/redis/go-redis/v9
 github.com/redis/go-redis/v9/internal
@@ -100,10 +100,10 @@ github.com/smira/go-statsd
 ## explicit; go 1.20
 github.com/stretchr/testify/assert
 github.com/stretchr/testify/require
-# go.uber.org/mock v0.3.0
+# go.uber.org/mock v0.4.0
 ## explicit; go 1.20
 go.uber.org/mock/gomock
-# golang.org/x/crypto v0.16.0
+# golang.org/x/crypto v0.17.0
 ## explicit; go 1.18
 golang.org/x/crypto/bcrypt
 golang.org/x/crypto/blowfish
@@ -149,7 +149,7 @@ google.golang.org/genproto/googleapis/api/httpbody
 # google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0
 ## explicit; go 1.19
 google.golang.org/genproto/googleapis/rpc/status
-# google.golang.org/grpc v1.60.0
+# google.golang.org/grpc v1.60.1
 ## explicit; go 1.19
 google.golang.org/grpc
 google.golang.org/grpc/attributes