fix(client): vendor cluster-key-slot to fix empty hash tag slot bug - #3431
Open
mishan wants to merge 1 commit into
Open
fix(client): vendor cluster-key-slot to fix empty hash tag slot bug#3431mishan wants to merge 1 commit into
mishan wants to merge 1 commit into
Conversation
Redis treats an empty hash tag ("{}") as no tag at all: it hashes the whole
key and stops searching. cluster-key-slot's single-pass scanner instead
falls through and keeps scanning for the next "}", hashing whatever sits
between - silently misrouting commands and, for sharded pub/sub, resolving
a channel to the wrong master so subscribed messages are never delivered.
Measured against a live Redis 8.0.6 cluster, this diverges from real slot
assignment 460 times across an 8016-key corpus.
Upstream (invertase/cluster-key-slot) was archived in March 2026 and had
been unmaintained for years before that, and @redis/client pins the exact
version "1.1.2" rather than a range, so no upstream fix would reach users
regardless. The function is ~40 lines plus a lookup table with no
dependencies, well within vendoring range.
Vendor the implementation into packages/client/lib/utils/calculate-slot.ts
with the fix applied: latch the whole-key fallback once an empty tag is
seen, so a later "}" elsewhere in the key can no longer be mistaken for a
tag close. lib/index.js in cluster-key-slot is byte-identical between
1.1.1 and 1.1.2, so this is the same fix applied to ioredis. Repoint
cluster/index.ts, cluster-slots.ts, client/pub-sub.ts, and
multi-shard-splitter.ts (plus the specs that computed expected slots) at
the vendored module, drop the npm dependency, and add unit tests covering
the empty-tag cases plus parity with upstream's own test suite.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Redis treats an empty hash tag ("{}") as no tag at all: it hashes the whole key and stops searching. cluster-key-slot's single-pass scanner instead falls through and keeps scanning for the next "}", hashing whatever sits between - silently misrouting commands and, for sharded pub/sub, resolving a channel to the wrong master so subscribed messages are never delivered. Measured against a live Redis 8.0.6 cluster, this diverges from real slot assignment 460 times across an 8016-key corpus.
Upstream (invertase/cluster-key-slot) was archived in March 2026 and had been unmaintained for years before that, and @redis/client pins the exact version "1.1.2" rather than a range, so no upstream fix would reach users regardless. The function is ~40 lines plus a lookup table with no dependencies, well within vendoring range.
Vendor the implementation into packages/client/lib/utils/calculate-slot.ts with the fix applied: latch the whole-key fallback once an empty tag is seen, so a later "}" elsewhere in the key can no longer be mistaken for a tag close. lib/index.js in cluster-key-slot is byte-identical between 1.1.1 and 1.1.2, so this is the same fix applied to ioredis. Repoint cluster/index.ts, cluster-slots.ts, client/pub-sub.ts, and multi-shard-splitter.ts (plus the specs that computed expected slots) at the vendored module, drop the npm dependency, and add unit tests covering the empty-tag cases plus parity with upstream's own test suite.
A detailed write up of this bug can be found at https://github.com/mishan/intraslot/blob/main/contrib/empty-hashtag-slot-bug/README.md
Note
Medium Risk
Changes core cluster slot hashing used for all keyed routing and sharded pub/sub; behavior shifts only for keys with empty
{}tags, but that path was previously wrong versus Redis.Overview
Replaces the archived
cluster-key-slotdependency with an in-treecalculate-slothelper and patches how empty hash tags ({}) are hashed so slot assignment matches Redis (whole-key hash, no further tag scanning). That bug could misroute cluster commands and sharded pub/sub channels to the wrong shard.Cluster routing (
cluster/index.ts,cluster-slots.ts), multi-shard splitting, and pub/sub slot logic now import the vendored module;cluster-key-slotis removed from@redis/clientdependencies. New unit tests cover empty-tag cases,generateMulti, and parity with upstream’s hash suite. The lockfile dropscluster-key-slotand bumps workspace@redis/clientto 6.2.1; rootpackage.jsonadds anallowScriptsentry for esbuild.Reviewed by Cursor Bugbot for commit b8107e4. Bugbot is set up for automated code reviews on this repo. Configure here.