timing.database¶
Attributes¶
Classes¶
Async SQLite store for raw step execution history and anomaly flags. |
Module Contents¶
- timing.database.logger¶
- class timing.database.StepTimingDatabase(db_path: str)¶
Async SQLite store for raw step execution history and anomaly flags.
The per-step hot path does zero disk I/O. On the Pi’s SD card the disk was the sole source of the “standstill between steps”: a per-step
record_executionSELECT that could stall behind the background writer’s WAL-checkpoint fsync (measured up to ~1.4 s on a single step). So the design is now “all in RAM during the run, persist once after it”:Reads never touch disk during the run. The whole history is bulk-seeded into per-signature in-memory windows once at
initialize()(before the run starts). After that the in-memory window is authoritative —fetch_recent_durations()and the anomaly baseline are pure RAM.Writes never touch disk during the run.
insert_execution()appends to an in-RAM buffer and updates the in-memory baseline synchronously (so read-after-write stays consistent). Nothing is committed on the step transition.The durable write happens once, after the run, when
flush()(oraclose()) is called from the run-completion path. All buffered rows go out in a singleexecutemany+commitwhile the robot is already idle, so the WAL-checkpoint fsync never lands mid-run.
A hard power-loss (process killed before
flush) drops that run’s timing rows, which is fine for diagnostic data — the trade the caller explicitly accepts to keep the run’s motion smooth.- db_path¶
- async initialize() None¶
Open the persistent connection and create the schema if needed.
If the existing database file is corrupted, it is deleted and recreated automatically. The whole history is bulk-loaded into the in-memory windows here (once, before the run) so no per-signature SELECT ever runs on the hot path, and durable writes are deferred to
flush().
- async fetch_recent_durations(signature: str, limit: int) list[float]¶
Return the most recent non-anomalous durations for a signature.
Served from the in-memory window (newest first), so this never touches disk on the hot path after the one-time seed.
- async insert_execution(signature: str, duration: float, anomaly: timing.models.AnomalyDetection | None) None¶
Record a completed execution.
Updates the in-memory baseline synchronously (so a subsequent read sees it) and buffers the durable write in RAM. Returns without touching disk — the step never waits on a commit; persistence happens in
flush()after the run.
- async flush() None¶
Persist all buffered executions to disk in one batched commit.
Called from the run-completion path (and by
aclose()/tests). This is the only place the step-timing history touches the disk on the hot connection, so the WAL-checkpoint fsync it may trigger lands while the robot is already idle, never between two steps.