pub struct StateStore { /* private fields */ }Expand description
Thread-safe state store with transition logging.
All reads and writes go through this store. Every write produces a
StateTransition record for audit and replay. Optionally backed by
a JSONL journal file for durability across process restarts (see
StateStore::durable).
Implementations§
Source§impl StateStore
impl StateStore
pub fn new() -> StateStore
Sourcepub fn durable(path: impl Into<PathBuf>) -> Result<StateStore, Error>
pub fn durable(path: impl Into<PathBuf>) -> Result<StateStore, Error>
Open a durable, JSONL-backed StateStore. If the file exists, its transitions are replayed (last-write-wins per key, with TTLs honored) to rebuild current state. Subsequent writes append to the same file.
Returns an error only on filesystem-level failures (parent directory missing, permission denied, etc.). Malformed lines inside the journal are skipped with a warning rather than failing the open — agent persistence shouldn’t refuse to start over a single bad line.
Sourcepub fn sync(&self) -> Result<(), Error>
pub fn sync(&self) -> Result<(), Error>
Fsync the journal writer. Call after a batch of writes when you need durability guarantees beyond best-effort flush.
Sourcepub fn reap_expired(&self, now: DateTime<Utc>) -> Result<Vec<String>, Error>
pub fn reap_expired(&self, now: DateTime<Utc>) -> Result<Vec<String>, Error>
Drop expired keys (per ttl_secs on their last write) and
rewrite the journal as a compacted snapshot of the surviving
state. Returns the keys that were reaped.
TTL semantics: a ttl_secs of 0 means “expired
immediately” — the key is reapable on the next call. There
is no “0 = forever” sentinel; use set (no TTL) for keys
that should never auto-expire.
Latest-write-wins: a key rewritten WITHOUT a TTL after a TTL’d write is NOT reaped — the more recent write effectively cancels the TTL.
Single-pass over the transitions log via a key→latest index, so cost is O(n) in journal length (not O(n²)).
pub fn get(&self, key: &str) -> Option<Value>
pub fn get_or(&self, key: &str, default: Value) -> Value
pub fn exists(&self, key: &str) -> bool
pub fn set(&self, key: &str, value: Value, action_id: &str) -> StateTransition
Sourcepub fn set_with_ttl(
&self,
key: &str,
value: Value,
action_id: &str,
ttl_secs: u64,
) -> StateTransition
pub fn set_with_ttl( &self, key: &str, value: Value, action_id: &str, ttl_secs: u64, ) -> StateTransition
Set a key with a TTL (seconds from now). reap_expired
drops the key once the deadline passes; re-setting the key
without a TTL (set) cancels the TTL.
ttl_secs == 0 means “expire immediately” (reapable on the
next reap_expired call). It is NOT a “no expiry” sentinel
— use the plain set(...) method for keys that should
never auto-expire. This differs from the Unix/Redis
convention; the distinction matters because a TTL passed
from untrusted input could otherwise silently mean
“forever” when the caller intended “never store.”
pub fn delete(&self, key: &str, action_id: &str) -> Option<StateTransition>
Sourcepub fn restore(&self, snapshot: HashMap<String, Value>, transition_count: usize)
pub fn restore(&self, snapshot: HashMap<String, Value>, transition_count: usize)
Restore state from a snapshot, truncating transitions.
pub fn transition_count(&self) -> usize
pub fn transitions(&self) -> Vec<StateTransition>
pub fn transitions_since(&self, index: usize) -> Vec<StateTransition>
pub fn keys(&self) -> Vec<String>
Sourcepub fn replace_all(&self, snapshot: HashMap<String, Value>)
pub fn replace_all(&self, snapshot: HashMap<String, Value>)
Replace the entire state map without recording transitions.
Used by checkpoint restore to avoid synthetic transition history.
Also clears the transitions log so callers of transitions_since()
don’t see stale history from the discarded state.
Sourcepub fn scoped<'a>(&'a self, tenant: Option<&'a str>) -> ScopedStateView<'a>
pub fn scoped<'a>(&'a self, tenant: Option<&'a str>) -> ScopedStateView<'a>
Build a tenant-scoped view over this store (Parslee-ai/car#187 phase 3 enforcement).
All reads / writes go through tenant:<tenant_id>:<key> so
distinct tenants can’t see each other’s keys. tenant = None
returns a view that hits the unscoped (legacy) namespace —
callers that don’t yet have a RuntimeScope get pre-#187
behaviour automatically.
Cheap to construct; holds a &self borrow plus the tenant
string. The view’s methods take the parking-lot lock the same
way the unscoped methods do.
Trait Implementations§
Source§impl Default for StateStore
impl Default for StateStore
Source§fn default() -> StateStore
fn default() -> StateStore
Source§impl StateView for StateStore
impl StateView for StateStore
Source§fn get_value(&self, key: &str) -> Option<Value>
fn get_value(&self, key: &str) -> Option<Value>
None if the key doesn’t exist.Source§fn key_exists(&self, key: &str) -> bool
fn key_exists(&self, key: &str) -> bool
Source§fn is_unknown(&self, _key: &str) -> bool
fn is_unknown(&self, _key: &str) -> bool
false by default — runtime state is always known.Auto Trait Implementations§
impl !Freeze for StateStore
impl !RefUnwindSafe for StateStore
impl Send for StateStore
impl Sync for StateStore
impl Unpin for StateStore
impl UnsafeUnpin for StateStore
impl UnwindSafe for StateStore
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more