Skip to content

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

Description

@insaf021

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions