#4685·jedis

Cluster slot for String keys ignores byte-level hash tag under a DBCS default charset

Author: insaf021Created Aug 15, 2026Updated Aug 18, 2026
Labelswill not fix

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

java
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));.