Skip to content

Make settings persistence atomic and validate timing invariants - #169

Open
damilolaedwards wants to merge 1 commit into
ethpandaops:mainfrom
damilolaedwards:fix-settings-persist-and-timing-validation
Open

Make settings persistence atomic and validate timing invariants#169
damilolaedwards wants to merge 1 commit into
ethpandaops:mainfrom
damilolaedwards:fix-settings-persist-and-timing-validation

Conversation

@damilolaedwards

Copy link
Copy Markdown
Contributor

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/config and pkg/webui/handlers/api still pass unchanged.

go build, go vet, and go test -race ./pkg/... all pass (aside from a
pre-existing, unrelated failure in pkg/webui caused by the frontend not
being built in this checkout).

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

1 participant