[Bug] Ignore migrating/importing slot markers when computing slot ownership in Robin
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 22/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- go
- Domain
- databases, distributed-systems
Research direction
Start in internal/redis/client.go with ParseSlotRanges and SlotsFullyCovered, then read the callers in internal/health/checker.go (slotsCovered, balanced) and internal/reconciler/health_reconciler.go (remediateSlotCoverage, resolveSlotDisagreement, resolveConfigDisagreement). The maintainers have not chosen between Option A and Option B, so agree on the approach before editing. Done means the new unit and fuzz tests pass and make lint, make test-all and make fuzz succeed.
Written by the indexing model from the issue text.
Description
Summary
redis.ParseSlotRanges returns an error for the whole slot field when the field contains a migrating/importing marker ([<slot>->-<nodeID>] or [<slot>-<-<nodeID>]). For example, "0-5460 [100->-abc]" fails to parse, and the valid range 0-5460 is discarded with it.
Several health-check and remediation paths handle that error by treating the node as having no slots, or by skipping it. While a slot is open, Robin can therefore misjudge slot coverage and balance. Remediation may then act on slots that already have an owner.
Related: #62 (slot range hardening). This issue was left out of #62's scope on purpose, because it changes behavior while migrations are in progress.
Background
- Redis adds migrating/importing markers only to the line flagged as
myself, that is, the node that holds the open slot. Other nodes do not show them. - A slot stays open while
redis-cli --cluster reshard/rebalanceis running. It can also stay open indefinitely if a migration is interrupted, for example when a pod restarts mid-rebalance. - During a migration, the slot still appears in the source node's regular ranges until
CLUSTER SETSLOT <slot> NODE <target>completes it. The target shows it only as[slot-<-id]. Ignoring the markers therefore still yields the correct ownership view. redis.SlotsFullyCovered(internal/redis/client.go) already relies on this: it skips[...]tokens before parsing.
Current behavior
When the health check's seed node has an open slot, its own CLUSTER NODES line contains a marker. Each caller of ParseSlotRanges then behaves as follows:
| Location | Function | Behavior with a marker |
|---|---|---|
internal/health/checker.go |
slotsCovered |
Returns false. SlotsCovered condition is a false negative (SlotsUncovered) |
internal/health/checker.go |
balanced |
Returns false. SlotsBalanced condition is a false negative (SlotsUnbalanced) |
internal/reconciler/health_reconciler.go |
remediateSlotCoverage |
Skips the primary. Its slots are treated as missing and CLUSTER ADDSLOTS is sent to other primaries |
internal/reconciler/health_reconciler.go |
resolveSlotDisagreement |
Ignores that node's vote for slot ownership |
internal/reconciler/health_reconciler.go |
resolveConfigDisagreement |
Ignores that node's vote for slot ownership |
internal/reconciler/health_reconciler.go |
remediateExcessPrimaries |
✅ Correct: checks for [ before parsing and treats the node as non-empty |
internal/redis/client.go |
SlotsFullyCovered |
✅ Correct: skips markers |
Potential impact (not yet reproduced)
The health remediation pipeline runs remediateSlotCoverage before remediateClusterCheck, which is the redis-cli --cluster fix step that actually repairs open slots. If the seed has an open slot, the following sequence looks possible:
slotsCoveredreturnsfalse, soremediateSlotCoverageruns.- The seed's own slots are reported as missing, and
CLUSTER ADDSLOTSis sent to another primary. - Redis replies
already busy, soresolveSlotDisagreementruns and forces ownership withCLUSTER SETSLOT ... NODEwhile a migration is still in progress.
This sequence comes from reading the code. It still needs to be confirmed with a test.
Proposal
Option A: make ParseSlotRanges skip markers
for _, token := range tokens {
if strings.HasPrefix(token, "[") {
continue // migrating/importing marker, not stable ownership
}
rng, err := parseSlotToken(token)
...
}
- Pros: smallest change. It fixes every caller at once and matches
SlotsFullyCovered. - Cons: it changes the contract of a shared function. Callers no longer get an implicit "migration in progress" signal from the error.
Option B (recommended): explicit helper for ownership
Add a helper that filters the markers. For example, redis.ParseOwnedSlotRanges(raw string) ([]SlotRange, error), or a StripSlotMarkers(raw string) string used before ParseSlotRanges. Use it explicitly in:
slotsCoveredbalancedremediateSlotCoverageresolveSlotDisagreementresolveConfigDisagreement
ParseSlotRanges stays strict, and remediateExcessPrimaries keeps its explicit check. Each call site is reviewed one by one, and the change touches fewer places.
Optional: skip slot coverage remediation while slots are in flight
In HealthReconciler.Reconcile, skip remediateSlotCoverage when redis.HasInFlightSlots(report.ClusterNodes) is true. Open-slot repair is then left to remediateClusterCheck (--cluster fix) and recoverOpenSlots, which are designed for it. This works with either option.
Tests
Unit:
slotsCoveredandbalancedwith a full, balanced topology in which themyselfline carries[slot->-id]/[slot-<-id]markers. Both should returntrue.remediateSlotCoveragewith the fakeclientFactory: when a primary has markers but covers its slots,ClusterAddSlotsmust not be called.resolveSlotDisagreementandresolveConfigDisagreement: the vote of a node whose slot field has markers must be counted.- New helper (option B), or updated
ParseSlotRanges(option A):- marker-only input returns no ranges;
- mixed input returns only the stable ranges;
- markers never hide a malformed regular token.
- Option A only: update
TestParseSlotRanges_RejectsUnsupportedTokens, which currently expects an error. - Fuzz: add an invariant that appending a marker never changes the parsed ownership.
Integration (envtest): not needed, because those tests do not use Redis slot data.
E2E: not strictly required. For end-to-end evidence, use an operator chaos scenario that interrupts a rebalance and leaves a slot open. Then check that Robin does not issue ADDSLOTS/SETSLOT for owned slots and that the cluster converges through --cluster fix.
Acceptance criteria
- With an open slot on the seed node,
SlotsCoveredandSlotsBalancedreflect the real stable ownership. -
remediateSlotCoveragenever sendsCLUSTER ADDSLOTSfor slots that are owned by a node with an open slot. - Slot ownership voting counts nodes that have open slots.
- Unit tests and fuzz invariants covering the above are added.
-
make lint,make test-allandmake fuzzpass.
- Dominant language
- Go
- Stars
- 18
- Forks
- 0
- Avg merge
- 11h 16m
- Merged PRs (30d)
- 11
Getting set up
- Ships a Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from InditexTech/redkey-robin
-
enhancement
Difficulty 4/5 3-5 days Newbie friendliness 55/100
InditexTech/redkey-robin#81 ·
Maintainers usually reply within 1 day
-
[Bug] Stale series for string-valued INFO metrics (e.g. `redkey_version`) after the value changesOpenbug
Difficulty 4/5 3-5 days Newbie friendliness 55/100
InditexTech/redkey-robin#76 ·
Maintainers usually reply within 1 day
-
[Bug] Rolling N+1 upgrade stalls indefinitely when a `redis-cli --cluster reshard` does not finishOpenenhancement
Difficulty 4/5 3-5 days Newbie friendliness 57/100
InditexTech/redkey-robin#70 · 1 comment ·
Maintainers usually reply within 1 day
-
enhancement
Difficulty 3/5 1-2 days Newbie friendliness 62/100
InditexTech/redkey-robin#46 ·
Maintainers usually reply within 1 day
-
enhancement
Difficulty 4/5 3-5 days Newbie friendliness 68/100
InditexTech/redkey-robin#44 ·
Maintainers usually reply within 1 day
All issues in InditexTech/redkey-robin
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
coder/openstorage#807 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
coder/coder-logstream-kube#255 · 1 comment ·
-
[FEATURE]: 录入 AIDEV Agent Package v1 接入参考文档Possibly taken A pull request linked to this issue is open or already merged. Openkind/features
Difficulty 2/5 1-3 hours Newbie friendliness 73/100
TencentBlueKing/bk-nodemgr#3991 ·
Maintainers usually reply within 1 day