pub trait StateStore: Send + Sync {
// Required methods
fn add(&self, key: &str, delta: u64) -> Result<u64, StateError>;
fn get(&self, key: &str) -> Result<u64, StateError>;
fn try_spend(
&self,
key: &str,
amount: u64,
limit: u64,
) -> Result<bool, StateError>;
fn try_spend_many(
&self,
spends: &[Spend],
) -> Result<Option<usize>, StateError>;
fn remove(&self, key: &str);
// Provided methods
fn refund(&self, key: &str, amount: u64) { ... }
fn remove_prefix(&self, prefix: &str) { ... }
}Expand description
Atomic counter operations. Every mutation is atomic with respect to
concurrent callers; try_spend is a single check-and-spend (never a
read-then-write).
Required Methods§
Sourcefn add(&self, key: &str, delta: u64) -> Result<u64, StateError>
fn add(&self, key: &str, delta: u64) -> Result<u64, StateError>
Add delta to key, returning the new value.
Sourcefn get(&self, key: &str) -> Result<u64, StateError>
fn get(&self, key: &str) -> Result<u64, StateError>
Read the current value of key (0 if absent).
Sourcefn try_spend(
&self,
key: &str,
amount: u64,
limit: u64,
) -> Result<bool, StateError>
fn try_spend( &self, key: &str, amount: u64, limit: u64, ) -> Result<bool, StateError>
Atomically spend amount from the remaining budget limit - spent(key).
Returns Ok(true) and records the spend if the full amount fits,
Ok(false) (recording nothing) otherwise.
Sourcefn try_spend_many(&self, spends: &[Spend]) -> Result<Option<usize>, StateError>
fn try_spend_many(&self, spends: &[Spend]) -> Result<Option<usize>, StateError>
Atomically validate and commit every spend, or commit none. Returns the index of the first dimension that would exceed its limit.
Every Spend in spends must carry a distinct key; two entries for
the same key would each observe the pre-commit value in the check
phase and pass their independent limit checks, then the commit phase
would sum them and blow through the cap. Duplicate keys return
StateError::Backend (not Overflow), matching the API-misuse class.
Provided Methods§
Sourcefn refund(&self, key: &str, amount: u64)
fn refund(&self, key: &str, amount: u64)
Return a previously-spent amount to a counter. Saturating: if
the stored value is below amount (e.g. a concurrent
remove_prefix cleared it first, or another refund already
covered part of the debt) the counter clamps at 0 rather than
underflowing into “negative budget”. Backends must never
propagate a refund error to the caller — the refund is
best-effort compensation on a lost-race path where the primary
verdict has already been decided. Default remove_prefix
semantics apply: backends with native TTL expiry may fold this
into their own cleanup if they prefer.
Round-33 F1: introduced to close the round-32 F3 concurrent-MCP
budget double-spend. When two identical MCP requests race and
one loses the atomic execution.claim(), the sandbox-gate
spend is refunded so payout_remaining and per-tool counters
reflect only the admitted work.
Sourcefn remove_prefix(&self, prefix: &str)
fn remove_prefix(&self, prefix: &str)
Remove every key beginning with prefix (whole-session cleanup at
finalization). Backends with native expiry (e.g. Redis TTLs) may
leave this as the default no-op; in-process backends must implement
it or session-keyed counters accumulate for the process lifetime.