Skip to main content

StorageHandle

Struct StorageHandle 

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

Handle returned by attach_snapshot_storage.

D246 r3: there is no RAII Drop. The observe subscription is torn down owner-side, synchronously, by calling StorageHandle::detach with the owner’s &Core (the graphrefly_core::OwnedCore borrow). StorageHandle::dispose remains a Core-free flag flip that stops late-fire persistence without unsubscribing (e.g. when the &Core is not in hand); detach is the full teardown.

Graph is now Send + Sync + 'static and Core-free; the held GraphObserveAllReactive owns a cheap Arc-clone of the graph (no 'g borrow), so StorageHandle is 'static.

Implementations§

Source§

impl StorageHandle

Source

pub fn dispose(&self)

Explicitly dispose: flip the per-tier disposed flag so the in-wave observe sink stops scheduling persistence. Core-free (no unsubscribe) — use StorageHandle::detach for the full owner-side teardown when the &Core is available.

F3 (QA 2026-05-14): on PoisonError (a panic in the observe sink poisoned the state mutex), recover via into_inner() rather than silently no-op. The disposed flag still gets set even after a sink panic, which matters for cleanup.

Source

pub fn detach(&self, core: &Core)

Owner-invoked, synchronous teardown (D246 r3 — replaces the retired RAII Drop). Flips the disposed flags then detaches the observe handle (unsubscribes every per-node sink, the namespace-change sink, and the topology sub) via the owner’s &Core. Idempotent: a second call is a no-op (the observe handle is taken on first call).

Source

pub fn flush_all(&self) -> Result<(), StorageError>

Drain pending writes on all tiers (D171).

When a tier is configured with debounce_ms > 0, attach_snapshot_storage’s observe sink writes via the tier’s save() but skips the inline flush(). Callers invoke this method — typically from a binding-side reactive timer subgraph (graphrefly_operators::temporal::interval, shipped in Slice T) — to commit the buffered writes to their backends. With debounce_ms == 0, the observe sink already force-flushes inline; calling this method is then a no-op because the tier’s pending buffers are empty.

Returns the first error encountered, if any. Successful tiers in the same call still flush; the error is surfaced for the caller’s diagnostics.

§Lock discipline (N3, QA 2026-05-14)

The state mutex is not held across tier.flush() calls. Holding it across blocking I/O would (a) serialize the reactive graph against backend latency (every observe-sink fire would wait on flush completion), and (b) deadlock if a caller invokes flush_all from inside an on_error callback (the observe sink already holds state.lock() when it invokes on_error). The implementation snapshots the per- tier flush requests under the lock, drops the lock, then runs the flushes in sequence. After each flush, a brief re-lock applies any per-tier state mutations (none today — flush() doesn’t return new state).

§Errors

Returns the first StorageError encountered. Subsequent errors are dropped (acceptable v1; the registered on_error callback fires per error in the observe sink path and is the canonical multi-error reporting surface).

F3: recovers from a poisoned state mutex via into_inner rather than silently returning Ok.

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, 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> 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 = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

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.