Skip to main content

SqliteStore

Struct SqliteStore 

Source
pub struct SqliteStore { /* private fields */ }
Expand description

The rusqlite-backed Store.

One connection behind a mutex. SQLite serialises writers anyway, so a connection pool would buy concurrency the database does not offer; what the mutex buys is Sync, so the agent can hold one handle across tasks.

Opened with synchronous = FULL and a request for WAL. FULL because this journal exists precisely to survive an unclean stop: a handful of fsyncs per runner attempt is not a cost worth trading for the chance of losing the last write before a power cut. WAL so that a reader — the TUI — does not block the agent’s journal writes.

The WAL half is a request, not a guarantee, which is why Self::journal_mode exists to report what actually happened. SQLite falls back to delete where the directory cannot host WAL’s shared-memory file, and says so in the pragma’s return row rather than by failing.

Implementations§

Source§

impl SqliteStore

Source

pub fn open(path: impl AsRef<Path>) -> Result<Self, StoreError>

Open (or create) the database at path and migrate it to SCHEMA_VERSION.

The path is used exactly as given. Resolving the platform application-data directory and creating it is d1’s job; a missing parent directory is reported rather than created.

§Errors

StoreError::Open when the file cannot be opened, StoreError::SchemaTooNew when the database came from a newer build, StoreError::Migration when a step fails.

Source

pub fn open_in_memory() -> Result<Self, StoreError>

An anonymous in-memory database, migrated to SCHEMA_VERSION.

For tests and for a dry run. It is private to this one connection — a second open_in_memory is a different database — so it cannot stand in for a file store in a test about two concurrent writers.

§Errors

As SqliteStore::open.

Source

pub const fn schema_version(&self) -> u32

The schema version this database is at.

Source

pub fn journal_mode(&self) -> &str

The journal mode this database is actually in, lowercased by SQLite.

wal for a healthy file store, memory for an in-memory one, and something else — delete, usually — where the directory cannot host WAL’s shared-memory file. That last case is not a failure but it does mean a reader blocks the agent’s journal writes, so it is worth showing an operator rather than assuming.

Source

pub fn readers_do_not_block_writers(&self) -> bool

Whether a reader can read this database without blocking the agent’s writes.

True exactly when Self::journal_mode is wal. An in-memory store is not included: it is private to one connection, so the question does not arise for it.

Source

pub fn path(&self) -> Option<&Path>

The path this store was opened from, or None for an in-memory one.

Source

pub fn clock_skew_repairs(&self) -> u64

How many attempt timestamps this store has repaired for backwards clock movement since it was opened.

See SqliteStore::normalise for what is repaired and why. A non-zero value means this machine’s clock stepped backwards while an attempt was in flight; each repair is also logged at warn.

Source

pub fn dump_text(&self) -> Result<String, StoreError>

Every row of every table, as text, in a deterministic order.

Two callers. An operator support bundle, and the security gate: the Definition of Done requires that “a grep of every fixture database and its dump finds no token-shaped value”, and a dump produced here is a testable artifact where a sqlite3 .dump invocation in a shell script is not.

This is safe to attach to a bug report because no column carries a credential, not because anything here redacts one. If a column ever does, this function becomes a disclosure and the schema is what has to change.

§Errors

StoreError::Sqlite on an I/O failure.

Trait Implementations§

Source§

impl Debug for SqliteStore

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Store for SqliteStore

Source§

fn put_host(&self, host: &Host) -> Result<(), StoreError>

Insert or replace this host. Read more
Source§

fn host(&self, id: HostId) -> Result<Option<Host>, StoreError>

One host, re-validated. Read more
Source§

fn hosts(&self) -> Result<Vec<Host>, StoreError>

Every host, re-validated. Read more
Source§

fn set_runner_root_override( &self, id: HostId, expected: Option<&LocalAbsolutePath>, new_root: Option<&LocalAbsolutePath>, expected_uncleaned: u16, ) -> Result<(), StoreError>

Move the configured runner root, and only that column, while both the override the caller read and the uncleaned ephemeral count it observed still hold. Read more
Source§

fn insert_policy(&self, policy: &ScalePolicy) -> Result<(), StoreError>

Add a policy that is not there yet. Read more
Source§

fn update_policy( &self, policy: &ScalePolicy, expected_revision: u64, ) -> Result<(), StoreError>

Write a changed policy, but only if nobody else changed it first. Read more
Source§

fn update_policy_confirming_active_count( &self, policy: &ScalePolicy, expected_revision: u64, expected_active: u16, ) -> Result<(), StoreError>

Atomically update a policy only while its revision and active-attempt count are exactly the values the caller observed. Implementations must evaluate both predicates in the same write transaction as the update; composing Store::attempts_for_policy and Store::update_policy does not satisfy this contract.
Source§

fn update_policy_confirming_uncleaned_count( &self, policy: &ScalePolicy, expected_revision: u64, expected_uncleaned: u16, ) -> Result<(), StoreError>

Atomically update a policy only while its revision and uncleaned attempt count are exactly the values the caller observed. Read more
Source§

fn remove_policy( &self, id: PolicyId, expected_revision: u64, ) -> Result<(), StoreError>

Delete a policy, subject to the same revision check as a write. Read more
Source§

fn policy(&self, id: PolicyId) -> Result<Option<ScalePolicy>, StoreError>

One policy, re-validated. Read more
Source§

fn policies(&self) -> Result<Vec<ScalePolicy>, StoreError>

Every policy, re-validated. Read more
Source§

fn record_attempt(&self, attempt: &RunnerAttempt) -> Result<(), StoreError>

Journal an attempt, inserting it or updating it in place. Read more
Source§

fn attempt(&self, id: AttemptId) -> Result<Option<RunnerAttempt>, StoreError>

One attempt, re-validated. Read more
Source§

fn attempts(&self) -> Result<Vec<RunnerAttempt>, StoreError>

Every attempt, oldest first. This is the input to e3’s startup recovery. Read more
Source§

fn attempts_for_policy( &self, policy_id: PolicyId, ) -> Result<Vec<RunnerAttempt>, StoreError>

Every attempt of one policy, oldest first. Read more
Source§

fn active_attempts_for_policy( &self, policy_id: PolicyId, ) -> Result<Vec<RunnerAttempt>, StoreError>

The attempts of one policy that still occupy a host capacity slot. Read more
Source§

fn uncleaned_attempts_for_policy( &self, policy_id: PolicyId, ) -> Result<Vec<RunnerAttempt>, StoreError>

The attempts of one policy that have not been cleaned, oldest first. Read more
Source§

fn slot_leases_for_policy( &self, policy_id: PolicyId, ) -> Result<Vec<RunnerAttempt>, StoreError>

The durable slot leases one policy holds, oldest first. Read more
Source§

fn uncleaned_ephemeral_attempts(&self) -> Result<Vec<RunnerAttempt>, StoreError>

Every uncleaned ephemeral attempt on this host, oldest first. Read more
Source§

fn remove_attempt(&self, id: AttemptId) -> Result<bool, StoreError>

Forget one attempt. Returns whether a row was removed. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more