Skip to content

fix(server-ng): stop redundant partition rebuilds resetting offsets - #3846

Open
numinnex wants to merge 1 commit into
masterfrom
fix_reconciler_rebuild
Open

fix(server-ng): stop redundant partition rebuilds resetting offsets#3846
numinnex wants to merge 1 commit into
masterfrom
fix_reconciler_rebuild

Conversation

@numinnex

@numinnex numinnex commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@github-actions github-actions Bot added the S-waiting-on-review PR is waiting on a reviewer label Aug 8, 2026
@codecov

codecov Bot commented Aug 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.64706% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 35.04%. Comparing base (01a64b2) to head (565ee61).

Files with missing lines Patch % Lines
core/server-ng/src/bootstrap.rs 0.00% 1 Missing ⚠️
core/server-ng/src/partition_reconciler.rs 98.57% 1 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##             master    #3846       +/-   ##
=============================================
- Coverage     76.58%   35.04%   -41.54%     
  Complexity     1046     1046               
=============================================
  Files          1347     1345        -2     
  Lines        171018   148900    -22118     
  Branches     142372   120330    -22042     
=============================================
- Hits         130967    52178    -78789     
- Misses        36233    95359    +59126     
+ Partials       3818     1363     -2455     
Components Coverage Δ
Rust Core 23.59% <97.64%> (-52.19%) ⬇️
Java SDK 63.67% <ø> (ø)
C# SDK 71.16% <ø> (-1.13%) ⬇️
Python SDK 88.70% <ø> (ø)
PHP SDK 82.97% <ø> (ø)
Node SDK 96.36% <ø> (+0.08%) ⬆️
Go SDK 69.18% <ø> (ø)
Files with missing lines Coverage Δ
core/partitions/src/iggy_partition.rs 77.45% <100.00%> (+0.04%) ⬆️
core/server-ng/src/partition_helpers.rs 67.07% <ø> (-0.07%) ⬇️
core/shard/src/lib.rs 47.90% <100.00%> (+0.13%) ⬆️
core/server-ng/src/bootstrap.rs 24.89% <0.00%> (ø)
core/server-ng/src/partition_reconciler.rs 93.01% <98.57%> (+0.18%) ⬆️

... and 736 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@hubcio hubcio left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix itself checks out. one blocker in the comments: the publish half has no test that can fail.

two pre-existing issues found while tracing, filing separately:

  • bootstrap.rs:1794: the failed-quarantine tombstone doesn't fence the reconciler (contains reads only namespace_map), so the namespace gets rebuilt - truncating the refused chain's segment 0 when the superblock frontier is 0 - and lands still-tombstoned with no untombstone path: unreachable forever, fast-skip pinned off. fence_partition_for_rebuild's failure arm hits the same wedge at runtime.

Some(0),
"the discarded op must not repoint the routing row"
);
assert_eq!(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stays green even with all three publish_current_offset() calls deleted - every build here adopts at offset 0 and the hand set_current_offset(3) lands after the pass. nothing else covers the publish half (vsr-off CI spawns the legacy binary). fix: partition.offset.store(N, Ordering::Release) before enqueuing InsertOwned, then assert stats.current_offset() == N - must fail when the publish is removed.

leaking its VSR group and segment writers"
);
assert_eq!(
shard.shards_table().shard_for(ns),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't fail - single shard, adopt would write ShardId::new(0) too. capture epoch_before after the first pass, stage the op with epoch_before + 1, assert epoch_for(ns) == epoch_before.

// An earlier pass already built this one and the pump has not applied it
// yet, so the `contains` test above reads false for finished work.
// Rebuilding is not a wasted-effort question: the second incarnation
// shares the namespace's `PartitionStats` with the live one and re-opens

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nothing is live here (contains was false) - the sibling is queued for adoption. same wording in the test doc at 1632; the copy at shard/lib.rs:1922 is fine.

Comment thread core/shard/src/lib.rs
// build already planted its initial segment over the live
// incarnation's path and folded that into the namespace's
// shared stats.
if partitions.contains(&namespace) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this drop is silent, but per the comment above it only fires after the build truncated the live writer's file - worth tracing::error! + a metric.

/// keys, so the row survives with the DEAD incarnation's `created_revision`. A
/// presence-only gate never refreshes it, and nothing else writes a non-owner's
/// row.
fn stage_routing_row(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

single caller, wrapping shards_table_has_epoch (also single caller), while 593 spells the same predicate inline. inline this, keep shards_table_has_epoch and use it at 593.

owning_shard: u16,
counters: &mut PassCounters,
) {
if shards_table_has_epoch(ctx, ns, epoch) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no mirror of the InsertOwned guard, so a lagging pump gets one duplicate InsertRouted per namespace per pass and the fast-skip stays disarmed. harmless - just worth a doc line that the asymmetry is deliberate (a queue scan here would go quadratic).

// building instead of aging its frames.
if ctx.shard.has_staged_insert_owned(ns) {
counters.already_staged += 1;
staged.insert(ns);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

staged duplicates what has_staged_insert_owned answers and goes stale across the reconcile_removals awaits. drop the set + staged_unapplied param and query the queue per parked namespace; parked early-returns when empty.

/// on the live incarnation, which then rejects every
/// `store_consumer_offset` above 0 with `InvalidOffset` until the next send
/// re-seeds it.
pub fn publish_current_offset(&self) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all three call sites funnel through IggyPartitions::insert - moving the publish inside makes the contract unforgettable, guards the unit-unreachable boot site through the same test, and lets this go crate-private.

@github-actions github-actions Bot added S-waiting-on-author PR is waiting on author response and removed S-waiting-on-review PR is waiting on a reviewer labels Aug 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-author PR is waiting on author response

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants