Fail open in consumer_has_unread when a consumer has no cursor rows - #429
Open
sergeivad wants to merge 1 commit into
Open
Fail open in consumer_has_unread when a consumer has no cursor rows#429sergeivad wants to merge 1 commit into
sergeivad wants to merge 1 commit into
Conversation
A no-sources MessagesGate reads consumer_cursors only, and the job it blocks is the very job whose poll creates the first cursor row — so with zero rows the gate could never open. A consumer reaches that state on a fresh install, and again whenever cleanup_expired_consumer_cursors deletes every row after a quiet stretch longer than the cursor TTL. With zero cursor rows, report unread whenever any messages exist at all: the job runs once, its poll seeds cursors at MAX(rowid) (backlog still skipped — "new consumers see only future messages" is delivery semantics and unchanged), and the check reverts to cursor-based. Established consumers (>=1 cursor row) still ignore untracked sources. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
The bug
A
MessagesGateconfigured without an explicit source list readsconsumer_cursorsonly. The job it gates is the very job whose poll creates the first cursor row — so with zero rows the gate can never open, and the job never runs to create them.A consumer reaches that state in two ordinary ways:
cleanup_expired_consumer_cursorsdeletes every row after a quiet stretch longer than the cursor TTL.The failure is silent: the job is simply skipped forever, and nothing in the logs says why.
The fix
With zero cursor rows, report unread whenever any messages exist at all. The job then runs once, its poll seeds cursors at
MAX(rowid), and the check reverts to the normal cursor-based path.Backlog is still skipped — "new consumers see only future messages" is delivery semantics and is unchanged. Consumers that already have at least one cursor row keep ignoring untracked sources exactly as before, so this only affects the state that was previously unrecoverable.
Tests
New coverage in
tests/test_cron_gates.pyandtests/test_db.pyfor both the zero-row and the established-consumer paths. Full suite green on top of currentmain.