pub struct InMemoryStore { /* private fields */ }Expand description
Single-node in-memory store: atomic counters behind a short transaction mutex that serializes check-and-spend so multi-key spends stay atomic.
Implementations§
Source§impl InMemoryStore
impl InMemoryStore
Trait Implementations§
Source§impl Debug for InMemoryStore
impl Debug for InMemoryStore
Source§impl Default for InMemoryStore
impl Default for InMemoryStore
Source§fn default() -> InMemoryStore
fn default() -> InMemoryStore
Source§impl StateStore for InMemoryStore
impl StateStore for InMemoryStore
Source§fn refund(&self, key: &str, amount: u64)
fn refund(&self, key: &str, amount: u64)
Saturating refund. saturating_sub on i64 keeps
the value non-negative even under concurrent remove_prefix
or a duplicate refund; the transaction lock keeps the
load/store pair atomic with respect to other spend / add
operations on the same key.
NEVER resurrect a cell that a concurrent
remove_prefix already dropped. The prior implementation
used self.cell(key) which materialises a fresh 0
AtomicI64 in the DashMap via entry().or_insert_with(...).
Under the lost-claim-plus-idle-close ordering
(mcp_call’s sandbox-gate debit races with the reconciler’s
clear_budget_state), the refund path would create a
permanent 0-cell for a sealed session that no future
remove_prefix would ever collect — attacker-driven memory
growth. Skip the refund silently when the cell is gone;
the “budget spent” state is already whatever the caller
wanted (probably 0) and there’s nothing to compensate.
Source§fn refund_many(&self, refunds: &[Refund])
fn refund_many(&self, refunds: &[Refund])
R66 F2: fold ALL listed refunds under a single acquisition
of transaction_lock so a concurrent try_spend_many (which
also holds this lock) cannot observe partial state. Under
the old shape (per-key refund), a concurrent try_tool_call
running between refund step 1 and step 2 could pass
total_calls (freshly refunded) but be refused by the per-
tool cap (not yet refunded) — an ordering result impossible
under the single-Lua RedisStore path. Now the two backends
serialise the batch identically.
Source§fn fetch_max(&self, key: &str, value: u64) -> Result<u64, StateError>
fn fetch_max(&self, key: &str, value: u64) -> Result<u64, StateError>
value, returning
the resulting maximum. Used for monotonic revocation cutoffs.Source§fn add(&self, key: &str, delta: u64) -> Result<u64, StateError>
fn add(&self, key: &str, delta: u64) -> Result<u64, StateError>
delta to key, returning the new value.Source§fn get(&self, key: &str) -> Result<u64, StateError>
fn get(&self, key: &str) -> Result<u64, StateError>
key (0 if absent).Source§fn try_spend_many(
&self,
spends: &[Spend],
) -> Result<TrySpendOutcome, StateError>
fn try_spend_many( &self, spends: &[Spend], ) -> Result<TrySpendOutcome, StateError>
Source§fn remove_prefix(&self, prefix: &str)
fn remove_prefix(&self, prefix: &str)
prefix (whole-session cleanup at
finalization). Every backend must implement this: native expiry
(e.g. Redis TTLs) is NOT a substitute, because a finalized session
id can be recycled into a fresh session within the TTL window and
would inherit the prior incarnation’s counters. In-process backends
additionally need it or session-keyed counters accumulate for the
process lifetime. Read moreSource§fn counter_ttl_secs(&self) -> Option<u64>
fn counter_ttl_secs(&self) -> Option<u64>
None means counters live for the process lifetime
(in-memory). Exposing it makes the prod/dev divergence
a VALUE callers can inspect (startup logs it; ops docs cite it)
instead of a doc comment — a session active longer than this
window has its budget counters silently reset against the
expiring backend only.Auto Trait Implementations§
impl !Freeze for InMemoryStore
impl !RefUnwindSafe for InMemoryStore
impl Send for InMemoryStore
impl Sync for InMemoryStore
impl Unpin for InMemoryStore
impl UnsafeUnpin for InMemoryStore
impl UnwindSafe for InMemoryStore
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
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§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