Problem
JedisClusterCRC16.getSlot(String) locates the {hash tag} by scanning for the { and } characters (through JedisClusterHashTag.getHashTag) and only encodes to bytes afterwards. The byte[] overload and the server's keyHashSlot() scan for 0x7B/0x7D in the encoded bytes.
Under a DBCS SafeEncoder.DEFAULT_CHARSET (GBK, Shift_JIS, Big5) a trail byte can be 0x7B/0x7D while the corresponding char is not a brace. The String path then picks a different tag (or none) than the wire bytes and computes a slot the server does not own. The same logical key also routes to two different slots depending on whether the String or the binary API is used.
getSlot(String) backs CommandArguments.getKeyHashSlots(), ClusterCommandObjects key routing and sharded pub/sub, so this affects routing for every keyed command, not just SCAN.
Minimal reproducible example
SafeEncoder.DEFAULT_CHARSET = Charset.forName("GBK");
// GBK bytes: 81 7B 41 81 7D -> a lead byte + '{' , 'A', a lead byte + '}'
String key = new String(new byte[]{(byte)0x81,0x7B,0x41,(byte)0x81,0x7D}, SafeEncoder.DEFAULT_CHARSET);
JedisClusterCRC16.getSlot(key); // 14821 (whole key hashed, no '{' char)
JedisClusterCRC16.getSlot(SafeEncoder.encode(key)); // 16212 (== server keyHashSlot)
- Single-key ops survive only via a
MOVED round-trip.
- Multi-key commands are mis-grouped: keys the server co-locates are rejected client-side (
Cannot get connection for command with multiple hash slots), and keys the client groups get a server-side CROSSSLOT.
Under UTF-8 there is no discrepancy (no multibyte sequence contains 0x7B/0x7D), so a fix is a no-op for the default configuration.
Practical benefit
Non-UTF-8 default charsets are a supported knob (SafeEncoder.DEFAULT_CHARSET, used for GBK/Shift_JIS/Big5 deployments). Making getSlot(String) scan the encoded bytes brings it in line with the binary overload and the server, so hash tags co-locate correctly on those deployments.
Proposed fix
Have getSlot(String) delegate to the byte[] overload so brace scanning happens on the wire bytes: return getSlot(SafeEncoder.encode(key));.
Problem
JedisClusterCRC16.getSlot(String)locates the{hash tag}by scanning for the{and}characters (throughJedisClusterHashTag.getHashTag) and only encodes to bytes afterwards. Thebyte[]overload and the server'skeyHashSlot()scan for0x7B/0x7Din the encoded bytes.Under a DBCS
SafeEncoder.DEFAULT_CHARSET(GBK, Shift_JIS, Big5) a trail byte can be0x7B/0x7Dwhile the corresponding char is not a brace. The String path then picks a different tag (or none) than the wire bytes and computes a slot the server does not own. The same logical key also routes to two different slots depending on whether the String or the binary API is used.getSlot(String)backsCommandArguments.getKeyHashSlots(),ClusterCommandObjectskey routing and sharded pub/sub, so this affects routing for every keyed command, not just SCAN.Minimal reproducible example
MOVEDround-trip.Cannot get connection for command with multiple hash slots), and keys the client groups get a server-sideCROSSSLOT.Under UTF-8 there is no discrepancy (no multibyte sequence contains
0x7B/0x7D), so a fix is a no-op for the default configuration.Practical benefit
Non-UTF-8 default charsets are a supported knob (
SafeEncoder.DEFAULT_CHARSET, used for GBK/Shift_JIS/Big5 deployments). MakinggetSlot(String)scan the encoded bytes brings it in line with the binary overload and the server, so hash tags co-locate correctly on those deployments.Proposed fix
Have
getSlot(String)delegate to thebyte[]overload so brace scanning happens on the wire bytes:return getSlot(SafeEncoder.encode(key));.