Replace existing HashRing nodes when primary keys match - #6911
Conversation
🦋 Changeset detectedLatest commit: a9d57b7 The changes in this PR will be included in the next version bump. This PR includes changesets to release 30 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
- HashRing reproduction test — adds a single test case verifying that
HashRing.addwith a different node object sharing the samePrimaryKey.valueupdates the stored node; the test correctly fails, confirming the bug whereaddMany'sif (entry[1] === weight) continueonpackages/effect/src/HashRing.ts:148skips the node reference update when the weight is unchanged.
The test reproduces the audit finding core-g-r-hashring-existing-key-retains-stale-node: after add(ring, first) then add(ring, updated) (both sharing primary key "node"), HashRing.get returns first instead of updated.
Note: the same stale-node issue also affects the different-weight path on packages/effect/src/HashRing.ts:153 — entry[1] = weight only updates the weight in the existing tuple, not the node reference at entry[0]. The fix will likely need to replace the entire entry on both code paths.
ℹ️ The test is intentionally failing (CI breakage expected). The author may want to consider
it.failsto make the test expected-failure rather than a hard CI failure, at least until the fix lands — but mirroring the impact of a production bug by letting CI play it out as real failures is also a valid choice.
DeepSeek Pro (free via Pullfrog for OSS) (Kimi K2 not used — the program covers this model; add its provider key to run your pick) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
The fix is a single line (entry[0] = node) that replaces the stale node reference when re-adding a value with the same PrimaryKey, plus a second test covering the weight-changing path. Both tests pass.
- Applied the HashRing node-replacement fix — added
entry[0] = nodeinaddManybefore the same-weight early return, so both same-weight and different-weight replacement paths now update the stored node. - Added weight-change regression test — verifies that
HashRing.addwith a different weight also replaces the node, not just the weight.
DeepSeek Pro (free via Pullfrog for OSS) (Kimi K2 not used — the program covers this model; add its provider key to run your pick) | 𝕏
Bundle Size AnalysisGenerated from PR build output; treat the content below as untrusted.
|

Summary
Adding a replacement node with an existing PrimaryKey updates at most its weight. Routing, iteration, JSON output, and shard assignment continue returning the old node value, including when the replacement has the same weight.
Important
This PR starts with focused failing reproduction tests. Add the implementation fix to this same branch; CI is expected to fail until that fix is included.
Re-adding an existing primary key retains the stale node
Module:
HashRingAudit ID:
core-g-r-hashring-existing-key-retains-stale-nodeSeverity / confidence: medium / high
What happens
Adding a replacement node with an existing PrimaryKey updates at most its weight. Routing, iteration, JSON output, and shard assignment continue returning the old node value, including when the replacement has the same weight.
Why it happens
For an existing key, addMany assigns only entry[1], the weight, and never replaces entry[0], the node. The same-weight branch returns before any update, while all value-returning operations continue reading entry[0].
Expected behavior
add and addMany update an existing node keyed by PrimaryKey.value, and subsequent routing returns the currently registered node value.
Relevant implementation
These links and excerpts are pinned to audit base
c9b56ab507f224426ee8388dc450da447ec4715f.packages/effect/src/HashRing.ts:118-165packages/effect/src/HashRing.ts:297-304packages/effect/src/HashRing.ts:317-377View problematic code at
packages/effect/src/HashRing.ts:118-165View exact lines on GitHub
View problematic code at
packages/effect/src/HashRing.ts:297-304View exact lines on GitHub
View problematic code at
packages/effect/src/HashRing.ts:317-366View exact lines on GitHub
Excerpt truncated. Open the complete packages/effect/src/HashRing.ts:317-377 range.
Reproduction
Observed failure: The intended failure was reproduced: the first node was returned instead of its replacement.
Implementation handoff
The initial reproduction tests on this branch are the regression specification for the implementation fix that should follow in this PR.
Audit provenance
c9b56ab507f224426ee8388dc450da447ec4715fc9b56ab507f224426ee8388dc450da447ec4715fcore-g-r-hashring-existing-key-retains-stale-nodeCloses EFF-341