Skip to content

Rebuild the ordered view on same-generation content-version re-resolutions - #680

Merged
Nick Tilton (NikTilton) merged 6 commits into
mainfrom
reconcile-log-stale-order
Aug 14, 2026
Merged

Rebuild the ordered view on same-generation content-version re-resolutions#680
Nick Tilton (NikTilton) merged 6 commits into
mainfrom
reconcile-log-stale-order

Conversation

@jschick04

@jschick04 Joseph Schick (jschick04) commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes a family of bugs where a same-generation, higher-ContentVersion re-resolution of a log (a Replace / LoadEvents, i.e. the raw store is rebuilt in place rather than appended) left the published ordered-view order and filter stale, and for row-removing re-resolutions could crash on out-of-range rows.

The ordered-view writer only re-sorted on an append (which grows coverage). A re-resolution that keeps the same generation but changes content was admitted into _latestReaders yet never triggered a re-sort, so the published snapshot kept the previous ordering. This lands as focused commits, each with its own review, sharing one branch and PR.

Commits

  1. 433cc14f same-count content-version replace - a same-count, higher-CV reader is unambiguously a replace (an append always grows count); signal a rebuild via a requiresRebuild out-param, with a _replaceAwaitingRebuild latch + SupersedeInFlight for a replace that races an in-flight rebuild.
  2. 7ff91f73 replace of a log entering the requested scope (S-1) - a log being replaced while entering a new view's scope is gated on the requested scope, not just the adopted scope.
  3. cb409aaf grow replace - an append and a grow-replace are indistinguishable by count+CV, so replacement provenance (isReplace) is threaded from the shadow effects through the writer command into the state; a grow-replace rebuilds, a live-tail append does not (no perf regression on the hot path).
  4. fa454c0a shrink replace - admits a previously-dropped row-removing re-resolution and makes it crash-safe: a single _liveIndexInvalidated flag quarantines both the live-insert loop and publishing until the shrunk index is adopted; coverage is decreased; build + catch-up loops clamp to the reader count; shrink-to-zero is handled; the rebuild routing latches only on an actually-pending build and otherwise force-rebuilds (bounded).
  5. ed7375bf test hardening - determinizes a pre-existing racy fault-state test (ATailReplayThatThrows...) that this PR surfaced under CI load: a gate forces the intended tail-replay path, and a bounded wait replaces an eager post-drain read. Test-only.
  6. 9bfd0de6 grow-replace via the coalesce/seed path - closes the last staleness gap: a coalesced view-request seed cannot observe replacement provenance, so any coalesced build that adopts now re-derives the order with a corrective rebuild (a grow-replace can no longer leave prior rows stale).

Testing

  • New ReconcileLogStaleOrderTests covering same-count, S-1, grow (replace vs append), shrink, shrink-to-zero, a gated concurrency harness (no fault when publishing/appending during the shrink rebuild window), and the coalesced grow-replace corrective rebuild.
  • Each fix was verified with discrimination bug-injects (defeating a single mechanism fails exactly the intended test(s) and nothing else).
  • Full EventLogExpert.Runtime.Tests: 2446 passed, 0 failed. Build clean.

Copilot AI lite review requested due to automatic review settings August 14, 2026 02:18

Copilot AI 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.

Pull request overview

Fixes stale ordered-view ordering/filtering and potential out-of-range faults when a log is re-resolved in-place (same generation, higher ContentVersion) by ensuring the ordered-view rebuilds appropriately for replace scenarios (including grow/shrink) and by hardening rebuild/catch-up logic around changing reader counts.

Changes:

  • Thread replacement provenance (isReplace) through shadow effects → writer → state, and trigger rebuilds for same-generation content replacements (including grow-replace vs append differentiation).
  • Add shrink-replace quarantine via LiveIndexInvalidated and clamp rebuild loops to reader counts to avoid out-of-range access during row-removing re-resolutions.
  • Add ReconcileLogStaleOrderTests covering same-count/grow/shrink replacements, in-flight rebuild supersession, and concurrent activity during shrink rebuild.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/Unit/EventLogExpert.Runtime.Tests/LogTable/OrderedView/ReconcileLogStaleOrderTests.cs Adds regression tests validating rebuild signaling and correct republishing across replace scenarios (same-count/grow/shrink) including concurrency windows.
src/EventLogExpert.Runtime/LogTable/OrderedView/OrderedViewWriter.cs Routes replace provenance into reconciliation, adds rebuild forcing/latching, and introduces invalidation retry behavior during shrink-replace rebuild windows.
src/EventLogExpert.Runtime/LogTable/OrderedView/OrderedViewState.cs Detects content replacements vs appends, updates coverage accordingly (including shrink), quarantines live index on shrink, and clamps rebuild iteration to reader counts.
src/EventLogExpert.Runtime/LogTable/OrderedView/OrderedViewShadowEffects.cs Passes isReplace correctly based on ingest/load modes when enqueueing reconcile commands.
src/EventLogExpert.Runtime/LogTable/OrderedView/OrderedViewScopeState.cs Adds SetCoverage to support coverage resets on content replacement (vs monotonic advance).
Suppressed comments (1)

src/EventLogExpert.Runtime/LogTable/OrderedView/OrderedViewWriter.cs:562

  • PublishNow() currently bails out only when LiveIndexInvalidated is set. When a replace arrives while a rebuild is already in-flight (_pendingRebuilds > 0), the writer sets _replaceAwaitingRebuild = true but does not prevent publishing; subsequent appends/flushes can still call PublishNow() and republish a snapshot that is known to be stale until the forced rebuild completes.
    private void PublishNow()
    {
        if (_state.LiveIndexInvalidated) { return; }

        _state.Publish();
        _dirty = false;
        _sincePublish = 0;
    }

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Copilot AI review requested due to automatic review settings August 14, 2026 15:08

Copilot AI 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.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

Suppressed comments (2)

src/EventLogExpert.Runtime/LogTable/OrderedView/OrderedViewWriter.cs:311

  • When a replace arrives during an in-flight rebuild, this path calls SupersedeInFlight(), which guarantees the current build’s adopt will be dropped as stale (generation mismatch). Letting the build continue is therefore wasted work and prolongs the time until the corrective rebuild can run (increasing the window where the ordered view can remain stale). Consider canceling the running build here, consistent with the restamp path (OrderedViewWriter.cs:517-519).
                        if (_pendingRebuilds > 0)
                        {
                            _replaceAwaitingRebuild = true;
                            _state.SupersedeInFlight();
                        }

tests/Unit/EventLogExpert.Runtime.Tests/LogTable/OrderedView/OrderedViewWriterFaultStateTests.cs:358

  • This early return; is redundant (the remaining code is only a local function declaration/comment) and can be confusing in a test because it looks like the test exits early. Removing it keeps the control flow straightforward while still allowing the local function to remain at the end of the method.
        return;

@jschick04
Joseph Schick (jschick04) marked this pull request as ready for review August 14, 2026 15:20
@jschick04
Joseph Schick (jschick04) requested a review from a team as a code owner August 14, 2026 15:20
@NikTilton
Nick Tilton (NikTilton) merged commit 84b7f45 into main Aug 14, 2026
8 checks passed
@NikTilton
Nick Tilton (NikTilton) deleted the reconcile-log-stale-order branch August 14, 2026 15:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants