Skip to main content

PersistentPoolStateStore

Struct PersistentPoolStateStore 

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

redb-backed persistent implementation of PoolStateStore.

Construct via PersistentPoolStateStore::open. One redb::Database per write family is owned for the lifetime of this value; drop closes every handle. The lane table is buffered in memory: record/forget mutate the working set only, and a caller must call PoolStateStore::flush to commit it to disk. The store is thread-safe — each redb file serialises its own writes via single-writer transactions, families commit on independent writer slots, and reads are MVCC.

The working set is a DashMap rather than one mutex-guarded map, so a record on the paid-delivery path locks only its own lane’s shard — no lane serialises on another, and settlement’s load_all/get do not contend with record (issue #1792 item 1). Each entry’s shard lock is the per-lane critical section that keeps a record/forget race decidable.

Implementations§

Source§

impl PersistentPoolStateStore

Source

pub fn open(data_dir: &Path, deployment: Deployment) -> Result<Self, StoreError>

Open (or create) the lane-state store under data_dir.

data_dir is validated through decdn_common::identity::ensure_data_dir before any redb operation, which enforces 0o700 on the directory and rejects insecure modes. The store file is then chmod’d to 0o600 after creation as defense-in-depth.

§Errors

When this returns Err, the caller MUST abort node bring-up — starting with a clean store silently forfeits the issue #527 guarantee.

deployment is the PaymentPool the node is configured against. The store binds to it before it hydrates any lane: a store stamped with another deployment drops its lane rows, pending settles and watcher checkpoints, and a store with no stamp takes this one and keeps its rows.

Source

pub fn path(&self) -> &Path

Filesystem path of the underlying database file. Useful for log messages and operator runbooks.

Trait Implementations§

Source§

impl Debug for PersistentPoolStateStore

Source§

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

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

impl KeyedCheckpointStore for PersistentPoolStateStore

Source§

fn load_checkpoint(&self, key: CheckpointKey) -> Result<Option<u64>, StoreError>

The last block scanned for key, or None on a never-written key (first-ever boot for that watcher — the caller applies its configured none-fallback floor). Read more
Source§

fn record_checkpoint( &self, key: CheckpointKey, block: u64, ) -> Result<(), StoreError>

Persist the last block scanned for key. Overwrites the prior value. Called as each backfill window and live tick advances, so the next boot resumes from here. Read more
Source§

fn flush_checkpoint(&self, key: CheckpointKey) -> Result<(), StoreError>

Force any buffered checkpoint for key to durable storage. The default is a no-op: implementations that already commit durably inside record_checkpoint have nothing buffered. A debouncing decorator overrides this to fsync the latest deferred block, and the runtime calls it on graceful shutdown so the most recent scan progress is not lost to the next boot’s rescan. Read more
Source§

impl PendingSettleStore for PersistentPoolStateStore

Source§

fn record_pending(&self, entry: &PendingSettle) -> Result<(), StoreError>

Persist a closed pool awaiting settlement. Overwrites any existing entry for the same pool (a re-close re-stamps the deadline). Read more
Source§

fn load_pending(&self) -> Result<Vec<PendingSettle>, StoreError>

Load every pool awaiting settlement. Called on each sweep tick (and once at bring-up) to find pools whose grace window has elapsed. Read more
Source§

fn forget_pending(&self, pool_id: B256) -> Result<(), StoreError>

Drop the pending-settle entry for a finalized pool. Idempotent; a no-op for an unknown pool. Read more
Source§

impl PoolStateStore for PersistentPoolStateStore

Source§

fn load_all(&self) -> Result<Vec<LaneState>, StoreError>

Every persisted lane, from the in-memory working set hydrated at open(). Tombstoned slots (a forget-ten lane not yet flushed) read as absent.

Source§

fn record(&self, state: &LaneState) -> Result<(), StoreError>

Advance the in-memory lane state and mark it dirty. Durability is the background flush’s job (ADR 003 §Off-chain voucher state persistence). Locks only this lane’s DashMap shard; concurrent records on other lanes proceed in parallel.

Source§

fn set_registered_until( &self, key: LaneKey, registered_until: u64, ) -> Result<(), StoreError>

Raise this lane’s observed on-chain registration expiry, monotonically — touches ONLY registered_until, never the replay-critical last_* tuple, so it cannot race a concurrent voucher record into a lost update. A no-op for a lane with no live record. Buffered like record; durability is the next flush’s job.

Source§

fn set_paid_cumulative( &self, key: LaneKey, paid_cumulative: U256, ) -> Result<(), StoreError>

Raise this lane’s paid cumulative, monotonically — touches ONLY paid_cumulative, never the replay-critical last_* tuple, so a PoolRedeemed write cannot race a concurrent voucher record into a lost update. A no-op for a lane with no live record (a redemption observed after the lane was forgotten has nothing to advance). Buffered like record; durability is the next flush’s job (#2052).

Source§

fn forget(&self, key: LaneKey) -> Result<(), StoreError>

Tombstone the lane so reads treat it as absent and the next flush deletes its on-disk row. Idempotent. The slot stays in the map (as a tombstone) so a concurrent record resurrecting the lane is decided under the same shard lock rather than racing a separate set. The lane’s owner-signed capability material (owner_sig) is part of the record, so it goes with the lane in the same delete.

Source§

fn flush(&self) -> Result<(), StoreError>

Write every dirty lane and apply every tombstone in ONE fsynced redb transaction. Each lane record carries its own owner-signed capability material, so nothing is written separately. Idempotent — a no-op when clean.

Double-buffered: the dirty work-list is drained and each lane’s slot is cloned out (a cheap memcpy — LaneState holds no heap fields), then encoded, all off the fsync’s critical path. The ~1–10 ms fsynced commit holds no lane shard lock, so a concurrent record (a single-shard map insert) never waits on disk I/O.

A record that lands after its key is drained pushes the key afresh and is captured by the next flush; the store only needs a monotonic floor, and the frontier is documented “safe to lose” on a crash (ADR 003 §Off-chain voucher state persistence), so a slightly older value now and a newer one one cadence later is correct.

Source§

fn get(&self, key: LaneKey) -> Result<Option<LaneState>, StoreError>

Point-lookup the persisted state for one lane, or None if no record exists. Used by the on-chain seller redemption path to read the latest accepted voucher (last_amount / last_bytes_delivered / last_signature) when deciding whether an accrued claim crosses the redemption threshold — always the freshest committed voucher. 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> FutureExt for T

Source§

fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ

Attaches the provided Context to this type, returning a WithContext wrapper. Read more
Source§

fn with_current_context(self) -> WithContext<Self> ⓘ

Attaches the current Context to this type, returning a WithContext wrapper. Read more
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> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> IntoRequest<T> for T

Source§

fn into_request(self) -> Request<T>

Wrap the input message T in a tonic::Request
Source§

impl<Unshared, Shared> IntoShared<Shared> for Unshared
where Shared: FromUnshared<Unshared>,

Source§

fn into_shared(self) -> Shared

Creates a shared type from an unshared type.
Source§

impl<L> LayerExt<L> for L

Source§

fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>
where L: Layer<S>,

Applies the layer to a service and wraps it in Layered.
Source§

impl<T> MaybeSend for T
where T: Send,

Source§

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

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
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<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

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