Skip to content

fix(filler): stop deriving the processing state from a one-way flag - #199

Merged
robrigo merged 1 commit into
mainfrom
fix/drop-live-processing-state-latch
Aug 25, 2026
Merged

fix(filler): stop deriving the processing state from a one-way flag#199
robrigo merged 1 commit into
mainfrom
fix/drop-live-processing-state-latch

Conversation

@robrigo

@robrigo robrigo commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Why

contract_readers.live goes true the first time a reader reaches head, and nothing ever writes it back. startProcessing() seeded the processing state from it, so a reader that once reached head started in head mode however far behind it restarted. The in-process promotion could not correct that, because it only fires while the state is still catchup, and the first commit wrote the flag out again. Every restart re-armed it. This has behaved the same way at every tag from 1.7.5 to 2.3.1.

Head mode is not free while a reader replays a backlog. Before 2.2.1 it published every trace and delta to redis inside the commit path, which is the defect #161 fixed from the other end, and it still forces a checkpoint write on every commit.

Starting in catchup costs nothing, because process() re-checks the same head-distance predicate on every block and promotes on the first one that qualifies, ahead of that block's commit and of its notifications. A reader genuinely at head is in head mode from its first block, so no notification and no checkpoint behaviour changes for a caught-up filler.

getReaderPosition() no longer returns the column either. Leaving it there with no consumer is what invites the latch back. reconcile is unaffected: it reads contract_readers with its own query, and its independent 60 second staleness check is what actually guards it.

Risk

This changes runtime behaviour on every filler, so it is deliberately separate from #198. The window it touches is the first block after a start. A reader at head promotes on that block before anything is committed or published, and a reader behind head was already meant to be in catchup and now is.

Validation

pnpm test goes from 419 passing to 420, 37 pending unchanged. The added test asserts setState is called with CATCHUP while the stored flag reads true, and it fails against the old line: restoring the seed and re-running gives 419 passing and 1 failing on exactly that test. pnpm check-types and pnpm lint are clean.

contract_readers.live goes true the first time a reader reaches head and
nothing ever writes it back, so seeding the processing state from it at
startup puts a reader that once reached head into head mode however far
behind it restarts. The in-process promotion cannot correct that, since
it only fires while the state is still catchup, and the first commit
writes the flag out again. Every restart re-arms it.

Head mode is not free while a reader replays a backlog. Before 2.2.1 it
published every trace and delta to redis inside the commit path, and it
still forces a checkpoint write on each commit.

Starting in catchup costs nothing instead, because process() re-checks
the same head-distance predicate on every block and promotes on the
first one that qualifies, ahead of that block's commit and of its
notifications. A reader that really is at head is therefore in head mode
from its first block. The flag is still written, since reconcile reads
it as a liveness hint, but nothing derives behaviour from it and
getReaderPosition no longer returns it.

Signed-off-by: Rob Konsdorf <rob@facings.io>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Starts fillers in catch-up mode instead of deriving state from the persistent one-way live flag.

Changes:

  • Initializes processing state as CATCHUP.
  • Removes live from reader-position queries.
  • Adds regression coverage for restarts with a stored live flag.

Reviewed changes

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

File Description
src/filler/receiver.ts Initializes processing in catch-up mode.
src/filler/receiver.test.ts Tests restart behavior with a stored live flag.
src/filler/database.ts Removes the unused live flag from position results.

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

@robrigo
robrigo merged commit db22153 into main Aug 25, 2026
8 checks passed
@robrigo robrigo mentioned this pull request Aug 25, 2026
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.

2 participants