fix(cassettes): order ATM state events, and reconcile the bay set #44
1 changed files with 20 additions and 0 deletions
fix(cassettes): drop bays the machine no longer reports
Some checks failed
ci.yml / fix(cassettes): drop bays the machine no longer reports (pull_request) Failing after 0s
Some checks failed
ci.yml / fix(cassettes): drop bays the machine no longer reports (pull_request) Failing after 0s
apply_reported_state upserted the positions in the payload and left every other row untouched. When a machine's bay count shrank, the stale row stayed: the dashboard showed a mix of old and new bays, and publish validation then rejected every operator payload for a position-set mismatch. The error text told the operator to fix it with atm-tui, which could not propagate either, so the only way out was DELETE FROM by hand. The payload is the machine's full bay set and the machine owns that layout, so positions absent from it are now deleted. The delete runs before the upserts: since this data layer commits per statement, a crash between the two leaves rows missing rather than stale, and the next heartbeat re-inserts them — the safe direction of the two. Refs #43 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
commit
106da5b46b
20
crud.py
20
crud.py
|
|
@ -1563,6 +1563,13 @@ async def apply_reported_state(
|
|||
Returns True if the state was applied, False if the event was not newer
|
||||
than what is already on file (see _should_apply_state_event).
|
||||
|
||||
The payload is the machine's full bay set, and the machine owns that
|
||||
layout — the bay count is hardware-determined. So positions absent from
|
||||
the payload are DELETED here. Leaving them behind was half the problem a
|
||||
shrinking bay count caused: the stale row stayed, the dashboard showed a
|
||||
mix of old and new, and every later publish was rejected for a position
|
||||
mismatch with no way out but hand-written SQL.
|
||||
|
||||
Populates both the operator-believed columns (denomination, count,
|
||||
updated_at, updated_by) and the reported columns (state_denomination,
|
||||
state_count, state_at, state_event_id), so the UI can show reported
|
||||
|
|
@ -1584,6 +1591,19 @@ async def apply_reported_state(
|
|||
if not _should_apply_state_event(oldest_state_at, event_created_at):
|
||||
return False
|
||||
|
||||
# Drop bays the machine no longer reports, before writing the rest. A crash
|
||||
# between the two leaves rows missing rather than stale, and the next
|
||||
# heartbeat re-inserts them — the safe direction.
|
||||
keep = sorted(payload.positions.keys())
|
||||
placeholders = ", ".join(f":p{i}" for i in range(len(keep)))
|
||||
delete_values: dict = {"mid": machine_id}
|
||||
for i, pos in enumerate(keep):
|
||||
delete_values[f"p{i}"] = pos
|
||||
await db.execute(
|
||||
"DELETE FROM spirekeeper.cassette_configs "
|
||||
f"WHERE machine_id = :mid AND position NOT IN ({placeholders})",
|
||||
delete_values,
|
||||
)
|
||||
|
||||
now = datetime.now()
|
||||
for pos, row in payload.positions.items():
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue