From 68c90f78de79804009a679820e885460c94d4cda Mon Sep 17 00:00:00 2001 From: Nedyalko Dyakov Date: Tue, 17 Jun 2025 11:43:28 +0300 Subject: [PATCH] chore(err): Extract crossslot err and add test --- error.go | 6 ++++++ osscluster.go | 5 ++--- osscluster_test.go | 14 ++++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/error.go b/error.go index 8c811966..8013de44 100644 --- a/error.go +++ b/error.go @@ -22,6 +22,12 @@ var ErrPoolExhausted = pool.ErrPoolExhausted // ErrPoolTimeout timed out waiting to get a connection from the connection pool. var ErrPoolTimeout = pool.ErrPoolTimeout +// ErrCrossSlot is returned when keys are used in the same Redis command and +// the keys are not in the same hash slot. This error is returned by Redis +// Cluster and will be returned by the client when TxPipeline or TxPipelined +// is used on a ClusterClient with keys in different slots. +var ErrCrossSlot = proto.RedisError("CROSSSLOT Keys in request don't hash to the same slot") + // HasErrorPrefix checks if the err is a Redis error and the message contains a prefix. func HasErrorPrefix(err error, prefix string) bool { var rErr Error diff --git a/osscluster.go b/osscluster.go index 06a58856..71c9a892 100644 --- a/osscluster.go +++ b/osscluster.go @@ -1506,9 +1506,8 @@ func (c *ClusterClient) processTxPipeline(ctx context.Context, cmds []Cmder) err cmdsMap := c.mapCmdsBySlot(cmds) // TxPipeline does not support cross slot transaction. if len(cmdsMap) > 1 { - err := fmt.Errorf("redis: CROSSSLOT Keys in request don't hash to the same slot") - setCmdsErr(cmds, err) - return err + setCmdsErr(cmds, ErrCrossSlot) + return ErrCrossSlot } if len(cmdsMap) == 0 { return nil diff --git a/osscluster_test.go b/osscluster_test.go index 7b0a8b29..80dfad7b 100644 --- a/osscluster_test.go +++ b/osscluster_test.go @@ -593,6 +593,20 @@ var _ = Describe("ClusterClient", func() { // Use hashtag to force all keys to the same slot. keys := []string{"A{s}", "B{s}", "C{s}", "D{s}", "E{s}", "F{s}", "G{s}"} assertPipeline(keys) + + // make sure CrossSlot error is returned + It("returns CrossSlot error", func() { + pipe.Set(ctx, "A{s}", "A_value", 0) + pipe.Set(ctx, "B{t}", "B_value", 0) + _, err := pipe.Exec(ctx) + Expect(err).To(MatchError(redis.ErrCrossSlot)) + }) + + // doesn't fail when no commands are queued + It("returns no error when there are no commands", func() { + _, err := pipe.Exec(ctx) + Expect(err).NotTo(HaveOccurred()) + }) }) })