From 3de5605ab2401c963647b63935049b1631121709 Mon Sep 17 00:00:00 2001 From: Vladimir Mihailenco Date: Thu, 28 Dec 2017 16:41:05 +0200 Subject: [PATCH 1/2] Pick random node when command does not have keys --- cluster.go | 14 ++++++++++---- cluster_test.go | 26 ++++++++++++++++++++++++++ command.go | 4 ++-- internal/hashtag/hashtag.go | 8 ++++++-- ring.go | 6 +++++- 5 files changed, 49 insertions(+), 9 deletions(-) diff --git a/cluster.go b/cluster.go index c81fc1d5..afaf3f35 100644 --- a/cluster.go +++ b/cluster.go @@ -533,16 +533,22 @@ func (c *ClusterClient) cmdInfo(name string) *CommandInfo { return info } +func cmdSlot(cmd Cmder, pos int) int { + if pos == 0 { + return hashtag.RandomSlot() + } + firstKey := cmd.stringArg(pos) + return hashtag.Slot(firstKey) +} + func (c *ClusterClient) cmdSlot(cmd Cmder) int { cmdInfo := c.cmdInfo(cmd.Name()) - firstKey := cmd.stringArg(cmdFirstKeyPos(cmd, cmdInfo)) - return hashtag.Slot(firstKey) + return cmdSlot(cmd, cmdFirstKeyPos(cmd, cmdInfo)) } func (c *ClusterClient) cmdSlotAndNode(state *clusterState, cmd Cmder) (int, *clusterNode, error) { cmdInfo := c.cmdInfo(cmd.Name()) - firstKey := cmd.stringArg(cmdFirstKeyPos(cmd, cmdInfo)) - slot := hashtag.Slot(firstKey) + slot := cmdSlot(cmd, cmdFirstKeyPos(cmd, cmdInfo)) if cmdInfo != nil && cmdInfo.ReadOnly && c.opt.ReadOnly { if c.opt.RouteByLatency { diff --git a/cluster_test.go b/cluster_test.go index 6f3677b9..43f3261b 100644 --- a/cluster_test.go +++ b/cluster_test.go @@ -536,6 +536,32 @@ var _ = Describe("ClusterClient", func() { Expect(nodesList).Should(HaveLen(1)) }) + It("should RANDOMKEY", func() { + const nkeys = 100 + + for i := 0; i < nkeys; i++ { + err := client.Set(fmt.Sprintf("key%d", i), "value", 0).Err() + Expect(err).NotTo(HaveOccurred()) + } + + var keys []string + addKey := func(key string) { + for _, k := range keys { + if k == key { + return + } + } + keys = append(keys, key) + } + + for i := 0; i < nkeys*10; i++ { + key := client.RandomKey().Val() + addKey(key) + } + + Expect(len(keys)).To(BeNumerically("~", nkeys, nkeys/10)) + }) + assertClusterClient() }) diff --git a/command.go b/command.go index 601a2882..598ed980 100644 --- a/command.go +++ b/command.go @@ -82,13 +82,13 @@ func cmdFirstKeyPos(cmd Cmder, info *CommandInfo) int { if cmd.stringArg(2) != "0" { return 3 } else { - return -1 + return 0 } case "publish": return 1 } if info == nil { - return -1 + return 0 } return int(info.FirstKeyPos) } diff --git a/internal/hashtag/hashtag.go b/internal/hashtag/hashtag.go index 2866488e..8c7ebbfa 100644 --- a/internal/hashtag/hashtag.go +++ b/internal/hashtag/hashtag.go @@ -55,13 +55,17 @@ func Key(key string) string { return key } +func RandomSlot() int { + return rand.Intn(SlotNumber) +} + // hashSlot returns a consistent slot number between 0 and 16383 // for any given string key. func Slot(key string) int { - key = Key(key) if key == "" { - return rand.Intn(SlotNumber) + return RandomSlot() } + key = Key(key) return int(crc16sum(key)) % SlotNumber } diff --git a/ring.go b/ring.go index a30c3210..0697b3e1 100644 --- a/ring.go +++ b/ring.go @@ -343,7 +343,11 @@ func (c *Ring) shardByName(name string) (*ringShard, error) { func (c *Ring) cmdShard(cmd Cmder) (*ringShard, error) { cmdInfo := c.cmdInfo(cmd.Name()) - firstKey := cmd.stringArg(cmdFirstKeyPos(cmd, cmdInfo)) + pos := cmdFirstKeyPos(cmd, cmdInfo) + if pos == 0 { + return c.randomShard() + } + firstKey := cmd.stringArg(pos) return c.shardByKey(firstKey) } From e5040d9ce782bd7e89c9eb686ed27395ddf94aeb Mon Sep 17 00:00:00 2001 From: Vladimir Mihailenco Date: Thu, 28 Dec 2017 17:00:22 +0200 Subject: [PATCH 2/2] Don't test in Go 1.4 --- .travis.yml | 2 -- ring.go | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index f49927ee..c95b3e6c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,6 @@ services: - redis-server go: - - 1.4.x - 1.7.x - 1.8.x - 1.9.x @@ -13,7 +12,6 @@ go: matrix: allow_failures: - - go: 1.4.x - go: tip install: diff --git a/ring.go b/ring.go index 0697b3e1..c11ef6bc 100644 --- a/ring.go +++ b/ring.go @@ -298,6 +298,9 @@ func (c *Ring) cmdInfo(name string) *CommandInfo { if err != nil { return nil } + if c.cmdsInfo == nil { + return nil + } info := c.cmdsInfo[name] if info == nil { internal.Logf("info for cmd=%s not found", name)