Repository navigation
refactor: Queue unification - #219
Merged
Antony Stubbs (astubbs) merged 37 commits intoApr 22, 2022
Merged
Conversation
Antony Stubbs (astubbs)
changed the base branch from
master
to
features/batching
March 8, 2022 23:30
Antony Stubbs (astubbs)
marked this pull request as draft
March 8, 2022 23:31
Antony Stubbs (astubbs)
force-pushed
the
features/batching
branch
6 times, most recently
from
March 14, 2022 15:26
b7b631c to
7e26075
Compare
Antony Stubbs (astubbs)
force-pushed
the
features/single-queue
branch
from
March 14, 2022 21:23
7a5b41f to
88ba43f
Compare
Antony Stubbs (astubbs)
referenced
this pull request
in astubbs/parallel-consumer
Mar 22, 2022
The simplest scenario of this issue, is: using PARTITION ordering max concurrency set to 2 - consumer is able to poll 10 messages, the first 5 for partition 0, the next 5 for partition 1 - when PC requests work from work manager, it first requests 2 work units - work manager then ingests 2 units from it's queue, but to shard partition 0 - then shard iteration begins, partition order restriction is set, so only takes a single entry from the ONLY partition shard of zero - so result is only a single record is returned, even through there are 5 in the queue for the 2nd partition that aren't "realised" - on next request to WM, 1 more unit is requested to hit concurrency target - and this is also ingested from the 3 remaining for partition 0, and the rest for partition 1 are still undiscovered, and so on and so on - extrapolate out... - solution: - ingest the entire work queue into shards every round, which is effectively also what #219 Also fixed by upcoming RR: refactor: Queue unification #219
Antony Stubbs (astubbs)
force-pushed
the
features/single-queue
branch
from
March 25, 2022 18:31
88ba43f to
a8463c9
Compare
Antony Stubbs (astubbs)
force-pushed
the
features/single-queue
branch
from
April 6, 2022 08:50
a8463c9 to
5f4bacd
Compare
Antony Stubbs (astubbs)
changed the base branch from
features/batching
to
master
April 6, 2022 08:54
PriorityQueue only provides a sorted `poll`, whereas TreeSet iterates in sorted order.
Antony Stubbs (astubbs)
force-pushed
the
features/single-queue
branch
from
April 6, 2022 08:55
5f4bacd to
4eeb008
Compare
Antony Stubbs (astubbs)
left a comment
Contributor
Author
There was a problem hiding this comment.
clean up all the dead code
…och tracking? Also propagates epochs more correctly, due to the queue unification, which enables a proactive epoch check before registering the work.
Antony Stubbs (astubbs)
left a comment
Contributor
Author
There was a problem hiding this comment.
...
Antony Stubbs (astubbs)
force-pushed
the
features/single-queue
branch
2 times, most recently
from
April 22, 2022 11:08
3b51ffe to
aa5c0e1
Compare
Antony Stubbs (astubbs)
marked this pull request as ready for review
April 22, 2022 11:26
40 of 64 tasks
Antony Stubbs (astubbs)
added a commit
to astubbs/parallel-consumer
that referenced
this pull request
Aug 18, 2026
…does NOT fix, and the untested retry queue Two open items that were being carried in conversation rather than anywhere a future session would find them. bug-epoch-check-is-per-batch-on-add-per-container-on-take.md states the confluentinc#909 defect as the asymmetry rather than the symptom: the add-side guard is evaluated once per BATCH against a PartitionState the caller already holds, while the take and complete checks are per CONTAINER against live state. A rebalance landing inside the registration loop therefore puts old-epoch containers into live shards. The reason it matters more than a missing re-check: 9a96686 (Queue unification, confluentinc#219) removed four synchronized (partitionStates) blocks on the stated grounds that "Synchronisation of PartitionState map no longer needed due to stronger epoch tracking ... a proactive epoch check before registering the work". That check was meant to be strong enough to REPLACE the lock. Per-batch and possibly-stale, it is not, and nothing since has re-established what the lock gave. The note says plainly that #31 fixes the consequence at the shard and does not repair this, and points at the filed repair and the two measured reasons it is not a one-liner. test-retry-queue-behaviour-untested.md records that RetryQueue has three tests, all covering ordering, and nothing covering the two-map uniqueness/ordering invariant, last-write-wins re-add with a changed retry-due time, revoke behaviour, or shard/queue consistency after a stale removal - the last being where the already-filed orphan lives. Worth a note because a defect there surfaces as records retried late or never, not as an exception, and the counters it feeds gate the poller throttle.
Antony Stubbs (astubbs)
added a commit
to astubbs/parallel-consumer
that referenced
this pull request
Aug 18, 2026
… where an agent editing the code will see them Two sessions in a row - both mine - read one checkpoint in isolation and concluded the epoch scheme was broken. It is not. Each checkpoint is deliberately partial and they are only sound together, and nothing in the code said so. PartitionState#epochIsStale now carries the scheme: register (proactive, once per poll batch), take (couldBeTakenAsWork, per container against LIVE state, and the authoritative one), complete (handleFutureResult, per container against live state). couldBeTakenAsWork and handleFutureResult cross-reference it rather than restating it, per the repo's never-state-a-fact-twice rule. It states the two things that were repeatedly re-derived as bugs. That checking once per batch is CORRECT, not a shortcut: RecordsAndEpoch carries one epochOfPartitionAtPoll for its whole record list, so a per-record check returns the identical answer. And that the checkpoint is knowingly racy - the caller looks the state up live and calls straight in, but a rebalance on the broker-poll thread can land between that lookup and the inserts, so the guard can pass wrongly either because the epoch bumped after a correct check or because this state object itself went stale. Checkpoint 2 is what makes that safe. It also says what NOT to do: re-checking per record or consulting the live epoch here closes nothing, since both remain check-then-act against a concurrent rebalance, and the lock that would close it was removed deliberately in 9a96686 (confluentinc#219) on the grounds that epoch tracking replaced it - reasoning that holds for the scheme as a whole, not for this checkpoint alone. Deletes docs/inflight/bug-add-side-epoch-check-can-compare-a-stale-state-against-itself.md. That note described a non-bug: its two "faults" share one window, backstopped by checkpoint 2. What was genuinely broken was the shard's collision handling, which is what this PR fixes. Chose javadoc over an architecture doc deliberately: only agents read this, and an agent editing the registration path sees javadoc without knowing to look for a doc.
Antony Stubbs (astubbs)
added a commit
to astubbs/parallel-consumer
that referenced
this pull request
Aug 19, 2026
… where an agent editing the code will see them Two review passes in a row read one checkpoint in isolation and concluded the epoch scheme was broken. It is not. Each checkpoint is deliberately partial and they are only sound together, and nothing in the code said so. PartitionState#epochIsStale now carries the scheme: register (proactive, once per poll batch), take (couldBeTakenAsWork, per container against LIVE state, and the authoritative one), complete (handleFutureResult, per container against live state). couldBeTakenAsWork and handleFutureResult cross-reference it rather than restating it. It states the two things that were repeatedly re-derived as bugs. That checking once per batch is CORRECT rather than a shortcut: RecordsAndEpoch carries one epochOfPartitionAtPoll for its whole record list, so a per-record check returns the identical answer. And that the checkpoint is knowingly racy, with checkpoint 2 being what makes that safe. It also says what NOT to do: re-checking per record or consulting the live epoch here closes nothing, since both remain check-then-act against a concurrent rebalance, and the lock that would close it was removed deliberately in 9a96686 (confluentinc#219) on the grounds that epoch tracking replaced it - reasoning that holds for the scheme as a whole, not for this checkpoint alone. Chose javadoc over an architecture doc deliberately: only agents read this, and an agent editing the registration path sees javadoc without knowing to look for a doc.
Antony Stubbs (astubbs)
added a commit
to astubbs/parallel-consumer
that referenced
this pull request
Aug 19, 2026
… where an agent editing the code will see them Two review passes in a row read one checkpoint in isolation and concluded the epoch scheme was broken. It is not. Each checkpoint is deliberately partial and they are only sound together, and nothing in the code said so. PartitionState#epochIsStale now carries the scheme: register (proactive, once per poll batch), take (couldBeTakenAsWork, per container against LIVE state, and the authoritative one), complete (handleFutureResult, per container against live state). couldBeTakenAsWork and handleFutureResult cross-reference it rather than restating it. It states the two things that were repeatedly re-derived as bugs. That checking once per batch is CORRECT rather than a shortcut: RecordsAndEpoch carries one epochOfPartitionAtPoll for its whole record list, so a per-record check returns the identical answer. And that the checkpoint is knowingly racy, with checkpoint 2 being what makes that safe. It also says what NOT to do: re-checking per record or consulting the live epoch here closes nothing, since both remain check-then-act against a concurrent rebalance, and the lock that would close it was removed deliberately in 9a96686 (confluentinc#219) on the grounds that epoch tracking replaced it - reasoning that holds for the scheme as a whole, not for this checkpoint alone. Chose javadoc over an architecture doc deliberately: only agents read this, and an agent editing the registration path sees javadoc without knowing to look for a doc.
Antony Stubbs (astubbs)
added a commit
to astubbs/parallel-consumer
that referenced
this pull request
Aug 19, 2026
… where an agent editing the code will see them Two review passes in a row read one checkpoint in isolation and concluded the epoch scheme was broken. It is not. Each checkpoint is deliberately partial and they are only sound together, and nothing in the code said so. PartitionState#epochIsStale now carries the scheme: register (proactive, once per poll batch), take (couldBeTakenAsWork, per container against LIVE state, and the authoritative one), complete (handleFutureResult, per container against live state). couldBeTakenAsWork and handleFutureResult cross-reference it rather than restating it. It states the two things that were repeatedly re-derived as bugs. That checking once per batch is CORRECT rather than a shortcut: RecordsAndEpoch carries one epochOfPartitionAtPoll for its whole record list, so a per-record check returns the identical answer. And that the checkpoint is knowingly racy, with checkpoint 2 being what makes that safe. It also says what NOT to do: re-checking per record or consulting the live epoch here closes nothing, since both remain check-then-act against a concurrent rebalance, and the lock that would close it was removed deliberately in 9a96686 (confluentinc#219) on the grounds that epoch tracking replaced it - reasoning that holds for the scheme as a whole, not for this checkpoint alone. Chose javadoc over an architecture doc deliberately: only agents read this, and an agent editing the registration path sees javadoc without knowing to look for a doc.
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.
Massive simplification, much improved semantics around poller notifying of work incoming.
Also address: