pub struct ScopedStateView<'a> { /* private fields */ }Expand description
Tenant-scoped view over a StateStore. All key arguments are
transparently prefixed with tenant:<tenant_id>: before hitting
the underlying store; on the way out, the prefix is stripped so
callers see their original keys.
Construct via StateStore::scoped. When tenant is None,
the prefix is empty and the view is functionally equivalent to
the unscoped methods on StateStore — useful for code paths
that always go through this view regardless of whether scope is
active.
§Isolation guarantee
Two views with distinct tenant strings cannot observe each
other’s writes through get / exists / keys. The transitions
log still records the full (prefixed) key so audit / replay sees
the actual storage layout.
§What isolation does not cover (phase 3 follow-ups)
StateStore::snapshot/restoreare deliberately unscoped — they’re called at proposal boundaries for rollback and need to see the whole map. Per-tenant partial rollback is a known concurrency hole when multiple proposals run interleaved; the pre-#187 baseline has the same issue, and fixing it cleanly requires either serializing per-tenant or extending the transactional model. Tracked as a follow-up.- The journal file (when durability is on) records full prefixed keys. Operators rotating tenants out can grep the journal by prefix.
Implementations§
Source§impl<'a> ScopedStateView<'a>
impl<'a> ScopedStateView<'a>
pub fn get(&self, key: &str) -> Option<Value>
pub fn get_or(&self, key: &str, default: Value) -> Value
Sourcepub fn snapshot(&self) -> HashMap<String, Value>
pub fn snapshot(&self) -> HashMap<String, Value>
Snapshot only this tenant’s namespace (EPIC E / E2) — the scoped
counterpart to StateStore::snapshot, safe to pair with
Self::restore for a tenant-isolated rollback.
Sourcepub fn snapshot_stripped(&self) -> HashMap<String, Value>
pub fn snapshot_stripped(&self) -> HashMap<String, Value>
Single-lock snapshot of this tenant’s namespace with the
tenant:<id>: prefix stripped — the map a reader surface (e.g. the
state.snapshot RPC) should return. Taken under ONE state-lock
acquisition, so it can never interleave with a concurrent mutation
batch the way a keys()-then-get() loop can — the result is the
complete old state or the complete new state, never a mix
(Parslee-ai/car#1140). Unlike Self::snapshot, the stripped keys
here do NOT round-trip through Self::restore.
Sourcepub fn restore(
&self,
snapshot: HashMap<String, Value>,
transition_count: usize,
) -> Result<RestoreDurability>
pub fn restore( &self, snapshot: HashMap<String, Value>, transition_count: usize, ) -> Result<RestoreDurability>
Restore only this tenant’s namespace from a scoped snapshot, leaving other tenants untouched (EPIC E / E2).
pub fn exists(&self, key: &str) -> bool
pub fn set(&self, key: &str, value: Value, action_id: &str) -> StateTransition
pub fn set_with_ttl( &self, key: &str, value: Value, action_id: &str, ttl_secs: u64, ) -> StateTransition
Sourcepub fn set_batch(
&self,
entries: Vec<(String, Value)>,
action_id: &str,
) -> Vec<StateTransition>
pub fn set_batch( &self, entries: Vec<(String, Value)>, action_id: &str, ) -> Vec<StateTransition>
Batch counterpart to Self::set — every entry applies inside one
atomic mutation boundary, with keys transparently prefixed into this
tenant’s namespace (see StateStore::set_batch).
pub fn delete(&self, key: &str, action_id: &str) -> Option<StateTransition>
Sourcepub fn keys(&self) -> Vec<String>
pub fn keys(&self) -> Vec<String>
Return keys belonging to this tenant only, with the
tenant:<id>: prefix stripped so callers see their original
key names. Unscoped views (no tenant) return only keys that
don’t start with tenant: — preventing accidental visibility
of scoped state through a legacy code path.