Conversation
…weep cursor Under step>1 the co-proc inspects every step-th key. The cleaner resumes each session at the key where the previous one stopped, and on wrap that key is the session's own start key - so the cursor is pinned and the same modulo class is inspected in every session while the remaining routes are never scanned (measured: 50% of routes permanently skipped at step 2, stable across 400/900 rounds). Dead routes in the skipped classes are never collected and the route table grows without bound. Advance a per-session phase offset (0..step-1) before the first inspection, so consecutive sessions visit every residue class. No protocol change: the phase is derived from a per-range session counter inside the co-proc. Fixes apache#300
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.
Summary
When the cleaner sweeps with
stepHint > 1, the co-proc inspects every step-th key beneath a per-range cursor. The cleaner resumes each session at the key where the previous one stopped, and when a session wraps it stops at its own start key — so the cursor is pinned and the same modulo class of the route table is inspected in every session. Routes in the remaining classes are never scanned and dead routes in them are never collected.Root cause
DistWorkerCoProc.gc()samples everystepUsed-th key and, on wrap, terminates when it returns tosessionStartKey; the recordednextStartKeyis therefore the session's own start key.DistWorkerCleanerseeds the next session from that key with the samestep, so the sampled residue class is fixed forever.Measured on a live broker under subscription churn: the same ~50% of routes stayed un-inspected across 400/900 consecutive GC rounds (a stable gap, not random).
Impact
Dead routes in the skipped classes are never collected; the route table (and the memory/cache working set) grows monotonically with subscribe/unsubscribe churn — the exact scenario the GC exists to bound.
Fix
Rotate a per-session phase offset (0..step-1) before the first inspection, so consecutive sessions visit every residue class:
No wire or protocol change — the phase is derived from a per-range session counter inside the co-proc. Worst case a key waits at most
stepsessions to be inspected, which is acceptable for a sampling sweep.Test evidence
New regression test (
gcSessionsRotatePhaseSoAllKeysGetInspected): 12 keys,stepHint = 2, successive sessions fed bynextStartKey; asserts every key is inspected within 8 sessions.Production validation
Deployed in a rolling 6-node cluster (wholesale
lib/replacement, one node at a time, zero rollbacks); post-deploy: cluster mesh fully re-established, all 13 live client connections reconnected, cross-node delivery verified end-to-end. Route-table size anddist.gc.*metrics are being tracked against a captured baseline.Fixes #300