Make settings persistence atomic and validate timing invariants - #169
Open
damilolaedwards wants to merge 1 commit into
Open
Make settings persistence atomic and validate timing invariants#169damilolaedwards wants to merge 1 commit into
damilolaedwards wants to merge 1 commit into
Conversation
Two related gaps in the settings service, both reachable through the same unauthenticated write path (the WebUI and Builder API share one HTTP port, unauthenticated unless an auth provider is configured): Persistence was per-key and silently lossy. SetMany wrote one state-db transaction per key and returned success unconditionally, even when a write failed or the process crashed partway through a batch. The in-memory config would already reflect the change, so a restart could silently revert a setting the caller had been told succeeded, or leave a batch half-applied with no way to tell which half. Validation covered almost nothing beyond schedule mode. A single write could invert the bid window (bid_start_time after bid_end_time) or push the reveal time past the slot deadline. Neither crashes anything - an inverted window just suppresses bidding and a late reveal is cleanly skipped - but both silently defeat the feature for the rest of the run. SetMany now stages a batch's changes, checks them against the resulting config's timing invariants, and persists everything in one transaction before applying anything to memory. A failure at any step leaves both memory and the state-db exactly as they were, and the caller gets the actual error instead of an unconditional nil. The same validation runs against CLI/config-file input at startup, right after slot-relative defaults are computed. db.Database gained PutSettings, a batch upsert in a single transaction, replacing the old per-row PutSetting.
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.
Problem
Two related gaps in the settings service, both reachable through the same
unauthenticated write path (the WebUI and Builder API share one HTTP port,
unauthenticated unless an auth provider is configured):
Persistence was per-key and silently lossy. SetMany wrote one state-db
transaction per key and returned success unconditionally, even when a write
failed or the process crashed partway through a batch. The in-memory config
would already reflect the change, so a restart could silently revert a
setting the caller had been told succeeded, or leave a batch half-applied
with no way to tell which half.
Validation covered almost nothing beyond schedule mode. A single write could
invert the bid window (bid_start_time after bid_end_time) or push the reveal
time past the slot deadline. Neither crashes anything - an inverted window
just suppresses bidding and a late reveal is cleanly skipped - but both
silently defeat the feature for the rest of the run.
Fix
SetMany now stages a batch's changes, checks them against the resulting
config's timing invariants, and persists everything in one transaction
before applying anything to memory. A failure at any step leaves both memory
and the state-db exactly as they were, and the caller gets the actual error
instead of an unconditional nil. The same validation runs against
CLI/config-file input at startup, right after slot-relative defaults are
computed.
db.Database gained PutSettings, a batch upsert in a single transaction,
replacing the old per-row PutSetting.
Testing
9 new tests across three files covering: atomicity under a forced real
SQLite failure, batch-all-or-nothing behavior, the validation rule table,
and the actual SetMany rejection path; plus db-level batch round-trip,
disabled no-op, and closed-connection failure cases. All existing tests in
pkg/configandpkg/webui/handlers/apistill pass unchanged.go build,go vet, andgo test -race ./pkg/...all pass (aside from apre-existing, unrelated failure in
pkg/webuicaused by the frontend notbeing built in this checkout).