fix(partitions): complete polls on the owning shard - #4119
Conversation
|
#4092 will be merged first :) |
|
@diegomrsantos have you joined our discord? if yes, what's your username? |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #4119 +/- ##
============================================
- Coverage 86.71% 86.31% -0.40%
+ Complexity 1457 1455 -2
============================================
Files 1268 1272 +4
Lines 212608 209121 -3487
Branches 177794 174306 -3488
============================================
- Hits 184356 180507 -3849
- Misses 23765 23877 +112
- Partials 4487 4737 +250
🚀 New features to boost your workflow:
|
b0f7638 to
c4e45d4
Compare
A disk poll that completes after purge can restore an offset from the deleted history and cause Next to skip fresh messages. Exercise the production disk read, purge, and offset paths with a deterministic schedule. Keep the regression intentionally failing and include a passing control with automatic commit disabled.
An offset from purged history can also fit within the fresh history, so an upper bound check alone cannot establish that consumer progress is valid. Cover both fresh history sizes and verify the complete disk read before purge. Describe the completion output as an automatic commit candidate. Keep the regressions failing until the production fix is implemented.
Document why the reproduction delays completion, reuses message offsets, and covers an old offset within the fresh range. Clarify that the tests observe automatic commit candidates before server admission.
Explain the old poll and fresh messages before discussing reused offsets, so the comments can be understood without tracing the helper first.
Reservations stay on the owning shard and belong to one consumer. Express those constraints in the token API and remove unnecessary ownership requirements from capacity admission. Extract synchronous poll admission and share queue cleanup while preserving history validation and overload behavior. Validated with affected crate tests on macOS and Linux, plus the Linux cluster consumer offset quota regression.
Poll completion must reject a full persistence queue before recording consumer progress or assigning an operation. Apply the persistence admission guard on the owning shard and cover rejection followed by retry. Adapt recovery and consumer group test fixtures to the current polling and persistence APIs, including reads through the shard message pump. Co-authored-by: Codex <noreply@openai.com>
c4e45d4 to
d89b81f
Compare
|
@diegomrsantos which one should be reviewed/merged first, this one or #4122? |
|
I’d prioritize this one, since it fixes the correctness issue. #4122 contains the benchmark tooling and works against master without this fix. Neither PR depends on the other, so they can be reviewed independently, but I’d merge this one first. |
Poll completion crosses asynchronous reads, owner admission, and offset replication, so maintainers need the lifetime and ordering contracts at these boundaries. Document the flow, history identity, capacity guards, and reply semantics on the relevant types, fields, and functions. Co-authored-by: Codex <noreply@openai.com>
|
Added a short overview of poll completion and concise docs for the new types, fields, and functions. These explain history validation, reservation lifetimes, admission checks, and why poll replies can precede durable offset commits. This update changes documentation only. |
Reservation bookkeeping can use Rc and Cell because guards stay on the owner thread, outside the shard inbox's Send requirement. Maintainers also need to preserve capacity checking and acquisition as one operation. Document these constraints on the completion message, reservation types, and capacity admission method. Co-authored-by: Codex <noreply@openai.com>
|
Added concise docs explaining the reservation ownership boundary. Previously, |
A missing owner reply does not prove that a poll was rejected: its eventual completion can still advance consumer progress. Returning an empty success hid that uncertainty. Return ShardCommunicationError when the owner reply is unavailable, clarify acceptance and delivery contracts, and cover timeout followed by an undeliverable rejection in a regression test.
Reads rejected before reaching the partition owner must remain safe for SDK retries. Return TransientNotAccepted for routing and inbox submission failures while preserving ShardCommunicationError for missing replies after submission. Merge current master, preserve both sets of regression tests, and cover refusal paths, lost replies, and consumer group ownership.
Groups record their last polled offset even when automatic commits are disabled. Keep a real disk read pending across purge and verify that its stale completion cannot restore group progress after new messages reuse the old offsets. Assert nonempty old results and both unset group offsets before a fresh read, then check that a fresh group completion succeeds without an automatic commit.
Blocking on automatic commit replication could deadlock a test with a paused replica send. Return an owned poll future and let the owner pump accept the read and continue replication. Exercise the helper with replication paused, then verify its commit reaches every replica after release. Update the simulator demo to drive the future and register its client before sending requests.
Exercise history validation after a completed group read passes through the real completion sender, inbox and owner pump. Replace the partition with new data at the same offsets and verify stale rejection preserves both group offsets before a fresh completion succeeds through the same route. Share resident message setup with the timeout test. Disabling the history check in an isolated copy makes the regression fail on stale last-polled progress.
A completion arriving after its caller times out could advance progress and make the next poll skip messages the caller never received. Check the reply connection before owner admission. Update the timeout regression to require unchanged progress, no queued or assigned automatic commit, and redelivery of the unseen messages. Document that cancellation racing with the check remains an unknown outcome. Validated with all 99 shard tests, strict shard Clippy, formatting checks, and the shard documentation build.
Recreating the shutdown receive on every pump turn repeats backoff and receive registration while the channel is empty. Pin the receive once and preserve its priority in the owner select loop. Cover shutdown wakeup after an ordinary completion, draining queued completions, and disconnection of the shutdown sender.
Missing owner replies used HTTP status 504 as the JSON error ID, hiding the Iggy error reported by binary transports. Keep HTTP 504 and render ShardCommunicationError with ID 11001. Cover timeout and TransientNotAccepted responses in HTTP serialization tests.
Stale poll completions triggered reservation reconciliation before their history was rejected, doing maintenance for a result that cannot be accepted. Check history and partition availability before reconciliation. Add a regression that preserves pending accounting after stale rejection and verifies a fresh completion still reconciles before admitting its commit.
History identity should remain unique without depending on allocation lifetime. A checked process counter prevents reuse when a partition is recreated. Represent histories as copied u64 values and allocate IDs only at the existing history creation points. Cover concurrent creation and counter exhaustion with correctness tests.
Preserve the owner completion path while incorporating the current master changes. The conflict was an upstream documentation correction inside the obsolete poll handler removed by this branch.
Completed reads should retain room to return to their owner when unrelated inbox traffic is saturated. Reserve a dedicated completion slot before disk I/O and retain it until dequeue or discard. Bound running reads plus queued results with the independent poll_completion_capacity setting, service both lanes fairly, and release reservations during shutdown and cancellation.
Disk polls carried unrelated metric handles, and partition offset state retained shared ownership that is no longer needed. Share frame drop metrics through existing completion lane state and own the durable offset table and capacity trackers directly. Document recovery from lost poll replies using a checkpoint for each partition. Validate with workspace Clippy, 509 affected unit tests and Rustdoc link checks. Formatting and TOML checks pass.
Make the minimum completion capacity explicit in fixtures and explain why one slot is sufficient.
spetz
left a comment
There was a problem hiding this comment.
Some final comments before it's ready to merge, could you also please confirm that the initial reports still hold after all the improvements?
An accepted poll can own an assigned prepare that has not reached the journal. Require its continuation to run immediately on the owning pump and assert that the partition remains available instead of silently losing the prepare. Report unexpected poll replies accurately and retire the obsolete automatic commit frame drop metric. Cover the continuation invariant with a regression that fails under the previous silent drop behavior.
spetz
left a comment
There was a problem hiding this comment.
LGTM, thank you for all the work!
|
/dst |
Fixes #4117.
A disk poll can remain pending while purge replaces a partition's message history and resets its offsets to zero. Previously, the detached read worker retained shared handles that could advance consumer offsets and group progress after purge. If fresh messages reused those numeric offsets, the stale completion could make a later
Nextpoll skip messages from the new history.Poll workers now return owned read results to the partition owner. The owner validates the captured history before accepting progress or authorizing a successful reply.
Implementation
PollHistoryIdis a process localu64identity from a checked counter. History replacement invalidates earlier reads, including when the namespace and numeric offsets are reused.sharding.poll_completion_capacityis independent of the ordinary inbox, and exhaustion rejects before reading.Verification
At
c7e6fb165, 1,348 affected tests pass on macOS and 1,371 on ARM64 Linux, with eight existing ignored tests on each platform. Strict Clippy with all features and all targets passes for the six affected crates on both platforms. Formatting, dependency sorting with--no-format, TOML formatting and the source diff checks pass. GitHub workflows for the measured commit require maintainer approval. The PR subsequently advanced to8f1c0e260through a master merge containing #4152; its changes are confined to connectors and their tests. The server code and build inputs used by this comparison are unchanged. The test and benchmark results below remain attributed toc7e6fb165.The new continuation regression proves an automatic commit prepare has been assigned but is not yet in the journal before tombstoning its namespace. It fails with the previous silent drop behavior and passes with the invariant restored. Existing suites cover delayed polls after purge, stale history with valid reused numeric offsets, consumer group progress, completion capacity, caller cancellation and reply release before replication.
The Linux boot smoke test uses
IGGY_SHARDING_CPU_ALLOCATION=allbecause the container denies NUMA memory binding. Partition and shard documentation, including private items, builds successfully. The broader server documentation build encounters existing broken links outside these review edits.Performance
The measured candidate
c7e6fb165was compared with upstream master585ab6922in the ARM64 Docker Desktop Linux VM. After high Docker memory use was reported, the existing benchmark was rerun with only its runtime container active and no concurrent builds. Earlier observations are retained separately and excluded from the rerun estimates.The rerun completed 120 measured runs and 2,304,000 successful nonempty polls, plus 20 excluded pilot runs. All workload and existing resource checks passed. There are 12 balanced randomized pairs per case. Positive values below mean slower polls.
Mean intervals use 20,000 bootstrap resamples of whole pairs. They are pointwise 95% intervals without adjustment across cases. p99 is the geometric mean of paired ratios of each run's p99, not a pooled percentile.
The concurrent write case has higher mean time, while resident reads have lower mean time. The other mean intervals span zero. The earlier Next p99 increase was not reproduced. This supports neither a blanket claim that all earlier reports still hold nor a claim that Docker memory caused their differences. Disk reads may use the guest OS cache, and these results describe this laptop VM. Native macOS performance and standalone disk batch and fsync results were not refreshed.
Full report, raw results, frozen methods and validation logs.
The benchmark tooling is reviewed separately in #4122. The earlier measurements and discussion remain historical evidence for their recorded revisions.
Metric retirement
frame_drops_total{variant="partition_auto_commit"}is retired because the inbox operation it measured no longer exists.frame_drops_total{variant="partition_poll_completion"}reports disk completion capacity and return failures, whilepartition_consumer_offsets_denied_totalcounts consumer offset capacity denials. These measure different events and are not a direct replacement for the retired label. Unexpected poll replies now log the actual reply instead of a missing reply field that was always false.