Skip to main content

QueryResource

Struct QueryResource 

Source
pub struct QueryResource<T, E = QueryError> { /* private fields */ }
Available on crate feature core only.
Expand description

Core state machine for a single query resource.

QueryResource owns the cache/request state for one resource. It tracks data, error, loading status, retry count, and a cooperative cancellation signal. Callers interact with it through lifecycle methods:

  1. begin_request — start a fetch
  2. accept_current_request — validate the request is still active
  3. complete_success / complete_failure — complete the request

This type is framework-free — it depends only on serde.

Implementations§

Source§

impl<T, E> QueryResource<T, E>

Source

pub fn is_loading(&self) -> bool

Whether the resource is currently loading.

Source

pub fn is_pending(&self) -> bool

Whether the resource is pending (no data yet).

Source

pub fn key(&self) -> &QueryKey

The cache key.

Source

pub fn status(&self) -> QueryStatus

Current status.

Source

pub fn data(&self) -> Option<&T>

Current data, if loaded.

Source

pub fn error(&self) -> Option<&E>

Current error, if any.

Source

pub fn active_request_id(&self) -> Option<RequestId>

Active request id, if a request is in flight.

Source

pub fn cache_policy(&self) -> CachePolicy

The cache policy.

Source

pub fn request_policy(&self) -> RequestPolicy

The request policy.

Source

pub fn started_at_ms(&self) -> Option<u64>

When the current request started (ms since UNIX epoch).

Source

pub fn last_updated_at_ms(&self) -> Option<u64>

When data was last updated (ms since UNIX epoch).

Source

pub fn cache_hits(&self) -> u64

Total cache hits.

Source

pub fn cancelled_count(&self) -> u64

Total cancelled requests.

Source

pub fn ignored_results(&self) -> u64

Total ignored (stale) results.

Source

pub fn has_data(&self) -> bool

Whether data exists.

Source

pub fn signal(&self) -> Option<&QuerySignal>

The cancellation signal, if a request is in flight.

Source

pub fn previous_data(&self) -> Option<&T>

Previous data (saved during optimistic updates for rollback).

Source

pub fn retry_count(&self) -> u32

Current retry count.

Source

pub fn retry_policy(&self) -> &RetryPolicy

The retry policy.

Source

pub fn increment_retry(&mut self)

Increment the retry counter.

Source

pub fn set_retry_policy(&mut self, policy: RetryPolicy)

Set the retry policy.

Source

pub fn reset_retry_count(&mut self)

Reset the retry counter to zero.

Source

pub fn set_cache_policy(&mut self, policy: CachePolicy)

Set the cache policy.

This allows policy updates on existing resources when use_query is called with the same key but different policies (e.g., a different TTL).

Source

pub fn set_request_policy(&mut self, policy: RequestPolicy)

Set the request policy.

This allows policy updates on existing resources when use_query is called with the same key but different request behavior.

Source§

impl<T, E> QueryResource<T, E>

Source

pub fn cache_age_ms(&self, now_ms: u64) -> Option<u64>

Cache age in milliseconds.

Source

pub fn is_cache_fresh(&self, now_ms: u64) -> bool

Whether the cache is fresh (within TTL).

For all policies with a TTL, this checks that data exists and the age is within the TTL window. The stale-while-revalidate window is NOT considered fresh — it is stale-but-serveable (see [is_stale_but_serveable]).

Boundary behavior: data at exactly TTL milliseconds old is considered fresh (age <= ttl_ms). Data older than TTL is stale (age > ttl_ms). This differs from HTTP Cache-Control: max-age where the boundary is exclusive. The inclusive boundary is chosen so that the fresh/stale partition is total: every age is either fresh or stale, with no gap.

Source

pub fn is_stale_but_serveable(&self, now_ms: u64) -> bool

Whether the cache is stale but still within the stale-while-revalidate window.

Returns true when:

  • The policy is StaleWhileRevalidate
  • Data exists
  • Data age is past TTL but within ttl_ms + stale_ms
Source

pub fn is_cache_expired(&self, now_ms: u64) -> bool

Whether the cache is fully expired (past the total valid window).

For StaleWhileRevalidate, this means past ttl_ms + stale_ms. For Ttl, this means past ttl_ms. For NoCache, always returns true (no data is ever valid).

Source

pub fn should_short_circuit_cache(&self, now_ms: u64) -> bool

Whether the cache can short-circuit (fresh data, no fetch needed).

Only returns true when the policy supports short-circuiting AND the data is within the TTL window (fresh, not stale).

Source

pub fn should_serve_stale_and_revalidate(&self, now_ms: u64) -> bool

Whether the resource should serve stale data while triggering a background refetch.

This is the core stale-while-revalidate check: data is past its TTL but still within the stale window. The caller should:

  1. Return existing data to the consumer immediately.
  2. Start a background fetch to revalidate.
Source

pub fn invalidate(&mut self)

Invalidate the cache (clear last-updated timestamp).

Data is retained but the resource is considered stale.

Source§

impl<T, E> QueryResource<T, E>

Source

pub fn complete_current_success( &mut self, request_id: RequestId, data: T, now_ms: u64, ) -> bool

Complete the current request with success by request id.

Convenience method that accepts + completes in one call. Returns true if the request was accepted.

Source

pub fn complete_current_failure( &mut self, request_id: RequestId, error: impl Into<E>, now_ms: u64, ) -> bool

Complete the current request with failure by request id.

Source

pub fn complete_current_optional_success( &mut self, request_id: RequestId, data: Option<T>, now_ms: u64, ) -> bool

Complete the current request with optional success by request id.

Source

pub fn complete_current_failure_with_data( &mut self, request_id: RequestId, data: T, error: impl Into<E>, now_ms: u64, ) -> bool

Complete the current request with failure but retain data by request id.

Source

pub fn complete_success(&mut self, guard: RequestGuard, data: T, now_ms: u64)

Complete with success, consuming the guard (two-phase protocol).

The guard is moved, preventing double-completion at the type level. Validates that no new request was started after the guard was issued.

Source

pub fn complete_failure( &mut self, guard: RequestGuard, error: impl Into<E>, now_ms: u64, )

Complete with failure, consuming the guard (two-phase protocol).

The guard is moved, preventing double-completion at the type level. Validates that no new request was started after the guard was issued.

Source

pub fn complete_success_optional( &mut self, guard: RequestGuard, data: Option<T>, now_ms: u64, )

Complete with optional success, consuming the guard.

If data is None, the status is set to QueryStatus::Idle rather than QueryStatus::Success to maintain the invariant that Success implies data exists.

Source

pub fn complete_failure_with_data( &mut self, guard: RequestGuard, data: T, error: impl Into<E>, now_ms: u64, )

Complete with failure but retain data, consuming the guard.

Validates that no new request was started after the guard was issued.

Source§

impl<T, E> QueryResource<T, E>

Source

pub fn should_clear_data_on_complete(&self) -> bool

Whether data should be evicted after observers consume it.

Returns true when CachePolicy::NoCache is set, meaning stored data will never be used for cache hits and should be cleared after delivery to avoid holding it in memory indefinitely.

Source§

impl<T, E> QueryResource<T, E>

Source

pub fn begin_request( &mut self, sequencer: &mut RequestSequencer, now_ms: u64, fetch_mode: QueryFetchMode, ) -> QueryBeginResult

Begin a new request on this resource.

Respects the cache policy (may return CacheHit) and request policy (IgnoreWhileLoading or LatestWins). When replacing an existing request, the old signal is cancelled so the in-flight fetcher can observe it and abort early.

Source

pub fn begin_request_with_id( &mut self, maybe_request_id: Option<RequestId>, now_ms: u64, fetch_mode: QueryFetchMode, ) -> QueryBeginResult

Like begin_request but accepts an optional pre-generated RequestId instead of using a RequestSequencer.

When maybe_request_id is Some, uses that ID directly (useful when the bucket’s co-located sequencer has already generated the ID). When None, falls back to the resource’s own stored sequencer so the generated ids are monotonic and collision-free across calls (N3) rather than every call producing a colliding RequestId(1,1).

This is the preferred entry point for the hook layer (audit fixes #1/#5/#15/#18): it allows the bucket’s persistent sequencer to provide globally unique, monotonically increasing RequestIds.

Source

pub fn is_current_request(&self, request_id: RequestId) -> bool

Whether the given request id is the current active request.

Source

pub fn accept_current_request( &mut self, request_id: RequestId, ) -> Option<RequestGuard>

Accept a request for completion.

Returns a RequestGuard if the request is still active, or None if it was replaced or cancelled. The guard is a capability token for the two-phase protocol (validate → complete).

Source

pub fn cancel(&mut self, error: E) -> bool

Cancel the active request.

Returns false if there is no active request. The signal is cancelled so the in-flight fetcher can observe it.

Data is preserved across cancellations. Current data (if any) is saved to previous_data before being cleared, allowing recovery via rollback_to_previous(). This matches TanStack Query behavior where cancelling a refetch does not destroy existing data.

When the resource was in LoadingEmpty status (no prior data existed), both data and previous_data remain None. When the resource was in LoadingWithData status (a refetch with existing data), the prior data is saved to previous_data and data is set to None. Callers can use rollback_to_previous() to recover the data if needed.

Source

pub fn mark_ignored_result(&mut self)

Source

pub fn is_data_stale(&self) -> bool

Whether the current data was served from stale cache (i.e., a stale-while-revalidate background refetch is in progress or failed).

Returns true when the resource has data but the status indicates the most recent fetch attempt failed or was cancelled. Consumers can use this to distinguish “fresh success” from “stale data still being displayed after a background refetch failure”.

Note: This is a heuristic check. A true result means data exists but the last fetch did not succeed — the data may still be perfectly valid.

Source

pub fn reset(&mut self)

Reset the resource back to idle, clearing state and diagnostic counters.

v2 fix: Cancels the signal before clearing it.

Preserves: cache_policy, request_policy, retry_policy, and key. These are considered configuration, not runtime state, and persist across resets. Use QueryResource::new() to create a fully fresh resource with default policies.

Calling reset() on an already-Idle resource resets diagnostic counters (cache_hits, cancelled_count, ignored_results, retry_count) to zero. This is intentional — reset() always resets counters regardless of current state. If counter preservation is needed, read them before calling reset().

Source

pub fn rollback_to_previous(&mut self) -> bool

Roll back to the previous data (optimistic update undo).

Clears any stored error to maintain the invariant that Success implies error is None (mirroring apply_success).

Source

pub fn set_data(&mut self, data: T)

Apply an optimistic update. Current data is saved for rollback.

Source

pub fn clear_data(&mut self)

Clear data optimistically. Current data is saved for rollback.

Transitions status to Idle to maintain the invariant that Success implies data is available (mirroring apply_success_optional’s None branch). Without this, a Success resource with data = None would panic on data.unwrap().

Source§

impl<T, E> QueryResource<T, E>

Source

pub fn new( key: impl Into<QueryKey>, cache_policy: CachePolicy, request_policy: RequestPolicy, ) -> Self

Create a new query resource with the given key and policies.

Trait Implementations§

Source§

impl<T: Clone, E: Clone> Clone for QueryResource<T, E>

Source§

fn clone(&self) -> QueryResource<T, E>

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<T: Debug, E: Debug> Debug for QueryResource<T, E>

Source§

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

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

impl<'de, T, E> Deserialize<'de> for QueryResource<T, E>
where T: Deserialize<'de>, E: Deserialize<'de>,

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl<T: Eq, E: Eq> Eq for QueryResource<T, E>

Source§

impl<T: 'static, E: 'static> ObservableResource for QueryResource<T, E>

Available on crate feature client only.
Source§

impl<T: PartialEq, E: PartialEq> PartialEq for QueryResource<T, E>

Source§

fn eq(&self, other: &QueryResource<T, E>) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl<T, E> Serialize for QueryResource<T, E>
where T: Serialize, E: Serialize,

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl<T: PartialEq, E: PartialEq> StructuralPartialEq for QueryResource<T, E>

Auto Trait Implementations§

§

impl<T, E> Freeze for QueryResource<T, E>
where Option<T>: Freeze, Option<E>: Freeze,

§

impl<T, E> RefUnwindSafe for QueryResource<T, E>

§

impl<T, E> Send for QueryResource<T, E>
where Option<T>: Send, Option<E>: Send,

§

impl<T, E> Sync for QueryResource<T, E>
where Option<T>: Sync, Option<E>: Sync,

§

impl<T, E> Unpin for QueryResource<T, E>
where Option<T>: Unpin, Option<E>: Unpin,

§

impl<T, E> UnsafeUnpin for QueryResource<T, E>

§

impl<T, E> UnwindSafe for QueryResource<T, E>

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<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Send + Sync>

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

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> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> Serialize for T
where T: Serialize + ?Sized,

Source§

fn erased_serialize(&self, serializer: &mut dyn Serializer) -> Result<(), Error>

Source§

fn do_erased_serialize( &self, serializer: &mut dyn Serializer, ) -> Result<(), ErrorImpl>

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
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