Skip to main content

QueryClient

Struct QueryClient 

Source
pub struct QueryClient { /* private fields */ }
Available on crate feature client only.
Expand description

Global registry for query and mutation resources.

Implements Global so it can be set once with cx.set_global(QueryClient::default()) and accessed from any component via cx.global::<QueryClient>().

§v2 Improvements

  • Default impl (no required params)
  • AHashMap for ~2x faster lookups on trusted keys
  • Actual mutation GC (not a no-op)
  • Collect-then-update pattern to avoid nested entity borrows

Implementations§

Source§

impl QueryClient

Source

pub fn infinite_resource<T: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static>( &mut self, key: impl Into<QueryKey>, cx: &mut App, ) -> Entity<InfiniteQueryResource<T, E>>

Get or create an infinite query resource for the given key and type pair.

Source

pub fn infinite_resource_with_policies<T: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static>( &mut self, key: impl Into<QueryKey>, cache_policy: CachePolicy, request_policy: RequestPolicy, cx: &mut App, ) -> Entity<InfiniteQueryResource<T, E>>

Get or create an infinite query resource with explicit policies.

Audit 3 fix (findings 3, 4): Graceful downcast recovery.

Source

pub fn infinite_query<T: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static>( &self, key: &QueryKey, ) -> Option<Entity<InfiniteQueryResource<T, E>>>

Get a specific infinite query entity by key.

Source

pub fn next_request_id_for_infinite_key<T: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static>( &mut self, key: &QueryKey, ) -> Option<RequestId>

Use the infinite query bucket’s co-located sequencer to generate a RequestId for an infinite query key.

Returns None if no bucket entry exists for the key. The sequencer is advanced in-place so subsequent calls produce monotonically increasing IDs. This is the infinite query equivalent of next_request_id_for_key.

Audit 3 fix (findings 3, 4): Graceful downcast recovery.

Source

pub fn all_infinite_queries<T: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static>( &self, ) -> Vec<Entity<InfiniteQueryResource<T, E>>>

Get all infinite query entities of a given type pair.

Source

pub fn register_mutation<V: Clone + Send + Sync + 'static, T: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static>( &mut self, entity: &Entity<MutationResource<V, T, E>>, cx: &App, )

Register a mutation entity.

Audit 3 fix (findings 3, 4): Graceful downcast recovery.

Source

pub fn all_mutations<V: Clone + Send + Sync + 'static, T: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static>( &self, ) -> Vec<Entity<MutationResource<V, T, E>>>

Get all mutation entities of a given type triple.

Source

pub fn invalidate_queries(&mut self, filter: &QueryKeyFilter<'_>, cx: &mut App)

Invalidate queries matching the filter.

Uses collect-then-update pattern to avoid nested entity borrows.

Source

pub fn reset_queries(&mut self, filter: &QueryKeyFilter<'_>, cx: &mut App)

Reset queries matching the filter.

Source

pub fn remove_queries(&mut self, filter: &QueryKeyFilter<'_>)

Remove queries matching the filter.

Source

pub fn cancel_queries(&mut self, filter: &QueryKeyFilter<'_>, cx: &mut App)

Cancel in-flight requests matching the filter (Audit 3, Finding 5).

Iterates all query and infinite query buckets, finds resources with active requests, and cancels them with a [QueryError::cancelled] error. This is essential for cleanup when navigating away from a page or when bulk cancellation is needed.

Equivalent to TanStack Query’s queryClient.cancelQueries(). Individual QueryResource::cancel() exists but this is the bulk cancellation method on the client.

Source§

impl QueryClient

Source

pub fn gc(&mut self, cx: &App)

Run garbage collection on all buckets.

Calls current_time_ms() internally to get the current time. If you already have a cached time value, use [gc_with_time] to avoid the syscall overhead (Audit 3, Finding 2).

Source

pub fn gc_with_time(&mut self, now_ms: u64, cx: &App)

Run garbage collection with a pre-computed time value (Audit 3, Finding 2).

Use this when you call GC frequently and want to amortize the cost of SystemTime::now() across multiple calls. The now_ms parameter should be milliseconds since the UNIX epoch (as returned by current_time_ms).

L5: sets self.last_gc_ms = now_ms at the top so a manual GC call debounces the next opportunistic GC sweep (otherwise the caller’s explicit gc() would not push back the MIN_GC_TIME_MS window and the next op could immediately re-trigger GC).

Source

pub fn diagnostics(&self, cx: &App) -> ClientDiagnostic

Get diagnostics for all queries and mutations.

Returns aggregate counts and per-resource diagnostic details. The queries and mutations vectors are populated by iterating all bucket entries, upgrading weak references, and reading entity state. Dead entries (collected entities) are skipped.

Audit 3 fix: Previously returned empty queries: Vec::new() and mutations: Vec::new() vectors. Now fully populates per-resource diagnostics via collect_diagnostics on each erased bucket.

Source

pub fn dehydrate(&self, cx: &App) -> DehydratedState

Available on crate feature persist only.

Serialize all cached query state into a portable format.

Extracts all live query resources, recording their keys, status, and type information. The resulting DehydratedState can be persisted to disk or stored for later restoration via [hydrate].

Only resources with Success status are included. Resources in Idle, Loading, Failure, or Cancelled states are skipped.

Note: Full data serialization requires type-specific code at the call site. Use get_query_data::<T, E>(key, cx) to extract typed data and serialize it externally. The DehydratedState provides the metadata (keys, type IDs) needed for typed restoration.

Source

pub fn hydrate(&mut self, _state: DehydratedState, _cx: &mut App)

Available on crate feature persist only.

Restore query state from a previously dehydrated snapshot.

Full hydration requires type-specific deserialization. The DehydratedState contains type_id keys but downcasting requires knowing the concrete types at the call site. Callers should iterate state.entries and call set_query_data::<T, E>() for each entry where they know the types.

This method is provided as a hook point for typed hydration and to document the intended API shape matching TanStack Query’s queryClient.hydrate().

Source

pub fn persist(&self, persister: &dyn QueryPersister, cx: &App)

Available on crate feature persist only.

Persist all cached data using the provided persister.

Dehydrates the current state and saves it via the persister. This can be called periodically (e.g., during GC) or on app shutdown to ensure cached data survives across app restarts.

Source

pub fn restore(persister: &dyn QueryPersister) -> Vec<DehydratedEntry>

Available on crate feature persist only.

Restore cached data from a persister.

Loads entries from the persister. Since type information is erased in the persister, callers must iterate and restore typed data themselves using set_query_data. This method loads the raw entries and returns them for inspection and typed restoration.

L4: this is an associated function rather than a method — it does not read any &self state, so callers invoke it as QueryClient::restore(&persister) instead of client.restore(...), avoiding the need for a borrow on the client.

Source

pub fn prepare_fetch_query<T: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static>( &mut self, key: impl Into<QueryKey>, cx: &mut App, ) -> Option<PreparedFetch<T, E>>

Prepare an imperative fetch for a query key, creating the resource if needed.

This creates (or reuses) the resource entity and begins a forced request, returning a PreparedFetch containing the entity, request ID, and signal. The caller is responsible for calling the fetcher and completing the request using complete_fetch or by directly calling complete_current_success / complete_current_failure on the entity.

This is the equivalent of TanStack Query’s queryClient.fetchQuery(). Unlike use_query, this does not subscribe or create an observer.

Returns None if the cache is fresh (cache hit) and no fetch is needed. In that case, use get_query_data to read the cached data.

§Example
use gpui_query::client::QueryClient;
use gpui_query::core::QueryKey;

if let Some(prepared) = client.prepare_fetch_query::<UserData, QueryError>(
    QueryKey::from("user/42"),
    cx,
) {
    // prepared.entity, prepared.signal, and prepared.request_id are now available.
    // Use cx.spawn() to run your async fetcher, then call
    // prepared.complete_success(data, cx) or prepared.complete_failure(e, cx).
}
Source

pub fn prepare_prefetch_query<T: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static>( &mut self, key: impl Into<QueryKey>, cache_policy: CachePolicy, request_policy: RequestPolicy, cx: &mut App, ) -> Option<PreparedFetch<T, E>>

Prepare a prefetch for a key that will be needed soon.

Creates the resource entity (or reuses an existing one) and begins a request if the cache is stale or empty. The resource is NOT subscribed – no observer is attached. When a component later calls use_query with the same key, it will find the prefetched data in the cache.

This is the equivalent of TanStack Query’s queryClient.prefetchQuery().

If the resource already has fresh data (cache hit), returns None. Use prepare_fetch_query with forced mode to override this behavior.

Returns a PreparedFetch containing the entity, request ID, and signal. The caller is responsible for calling the fetcher and completing the request.

Source§

impl QueryClient

Source

pub fn register_serializer<T, E>(&mut self, f: fn(&T) -> JsonValue)
where T: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static,

Register a serializer for resources of type (T, E).

Only resources whose T has a registered serializer are emitted by the value-carrying collect_persistable_into path; unregistered types fall back to metadata-only (skipped), matching the legacy dehydrate.

f is a fn(&T) -> serde_json::Value (a plain function pointer, not a closure) so it is Send + Sync + 'static withoutboxing overhead.

Source

pub fn register_deserializer<T, E>( &mut self, deserialize: fn(&JsonValue) -> Option<T>, )
where T: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static,

Register a deserializer for resources of type (T, E), enabling hydrate to re-prime on-disk values of this type.

Strict-deserializer contract. hydrate offers every on-disk entry to every registered deserializer (there is no type discriminator on PersistedEntry, so routing is by trial). A deserializer MUST return None for any JSON shape it does not recognize as its own T; only return Some for values that genuinely decode to T. A lax deserializer that accepts a foreign shape would mis-prime the wrong bucket. (Each (T, E) writes to its own bucket, so typed data is not clobbered, but a permissive decoder wastes work and can prime a stale value.) Keep deserializers strict and cheap.

Source

pub fn collect_persist_snapshot( &self, filter: &PersistFilter, max_age: Duration, cx: &App, ) -> PersistSnapshot

Collect a value-carrying snapshot from the live cache, honoring filter and max_age. Only resources with a registered serializer (and in Success status) are included.

Source

pub fn persist_with<P: Persister>( &self, persister: P, opts: PersistOptions, cx: &mut App, ) -> PersistHandle

Drive a Persister from the live cache, debounced on the CacheMutation dirty signal.

On every CacheMutation bump, the callback:

  1. collects a fresh PersistSnapshot (cheap; main thread, has &App),
  2. stashes it in a shared slot, replacing any pending snapshot,
  3. spawns a debounced task that, after opts.debounce, takes the latest snapshot from the slot and runs persister.save(&snapshot) on the background executor.

Rapid bursts coalesce: only the most recently collected snapshot is saved when the debounce timer elapses. Returning the PersistHandle keeps the observation alive; dropping it stops further saves.

Source§

impl QueryClient

Source

pub fn new() -> Self

Create a new client with default policies.

Source

pub fn with_policies( default_cache_policy: CachePolicy, default_request_policy: RequestPolicy, ) -> Self

Create with custom default policies.

Source

pub fn with_gc_time(self, gc_time_ms: u64) -> Self

Set the garbage collection time (in milliseconds).

Values below 1000ms are clamped to 1000ms during GC to prevent aggressive eviction of all Idle/Failure resources on every GC pass.

Source

pub fn resource<T: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static>( &mut self, key: impl Into<QueryKey>, cx: &mut App, ) -> Entity<QueryResource<T, E>>

Get or create a query resource for the given key and type pair.

Source

pub fn resource_with_policies<T: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static>( &mut self, key: impl Into<QueryKey>, cache_policy: CachePolicy, request_policy: RequestPolicy, cx: &mut App, ) -> Entity<QueryResource<T, E>>

Get or create a query resource with explicit policies.

Audit 3 fix (findings 3, 4): Uses graceful downcast recovery instead of expect(). On type mismatch, logs the type name and creates a fresh bucket, preventing application crashes from hypothetical TypeId collisions.

Source

pub fn all_queries<T: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static>( &self, ) -> Vec<Entity<QueryResource<T, E>>>

Get all query entities of a given type pair.

Source

pub fn query<T: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static>( &self, key: &QueryKey, ) -> Option<Entity<QueryResource<T, E>>>

Get a specific query entity by key.

Source

pub fn next_request_id_for_key<T: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static>( &mut self, key: &QueryKey, ) -> Option<RequestId>

Use the bucket’s co-located sequencer to generate a RequestId for a key.

Returns None if no bucket entry exists for the key. The sequencer is advanced in-place (mutated) so subsequent calls produce monotonically increasing IDs. This is the fix for audit findings #1/#5/#15/#18: using the bucket’s persistent sequencer instead of a transient one prevents every request from getting the same RequestId(1, 1).

Audit 3 fix (findings 3, 4): Graceful downcast recovery.

Source

pub fn get_query_data<T: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static>( &self, key: &QueryKey, cx: &App, ) -> Option<T>

Read the cached data for a query key directly, without going through a hook.

Returns None if no resource exists for the key, the entity was collected, or the resource has no data (has not completed a fetch).

This is the ergonomic equivalent of TanStack Query’s queryClient.getQueryData(key).

Source

pub fn with_query_data<T: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static, R>( &self, key: &QueryKey, cx: &App, f: impl FnOnce(&T) -> R, ) -> Option<R>

Read the cached data for a query key via a borrow callback, with NO clone of T (audit fix #L12).

This is the zero-clone counterpart to get_query_data: instead of returning Option<T> (which clones the value out of the resource), it hands f a &T for the duration of the call. Use this when the caller only needs to inspect the cached data (e.g. compute a derived value, render a summary) and would otherwise pay for a full T::clone() it discards immediately.

Returns None if no resource exists for the key, the entity was collected, or the resource has no data. Returns Some(R) (the value produced by f) otherwise. T and E are unchanged from get_query_data; R is the closure’s return type and is independent of T, so it does not shadow the crate’s T/E conventions.

Source

pub fn set_query_data<T: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static>( &mut self, key: impl Into<QueryKey>, data: T, cx: &mut App, )

Write data directly into the cache for a query key, creating the resource if it does not already exist.

This is the ergonomic equivalent of TanStack Query’s queryClient.setQueryData(key, data). The resource’s previous data is saved for rollback via rollback_to_previous(). The data is set via set_data() which saves previous data but does not change the resource’s status or timestamp. Use this for optimistic updates and manual cache manipulation where you control the lifecycle.

Trait Implementations§

Source§

impl Default for QueryClient

Source§

fn default() -> Self

Audit fix #21: Explicit Default impl that sets gc_time_ms to 300_000 (5 minutes), matching with_policies. The previous derive produced gc_time_ms: 0, which silently disabled GC — every non-loading Idle/Failure resource would be evicted on every pass. All other field defaults are identical to what the derive produced.

Source§

impl Global for QueryClient

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<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> 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> 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> NoneValue for T
where T: Default,

Source§

type NoneType = T

Source§

fn null_value() -> T

The none-equivalent value.
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> ReadGlobal for T
where T: Global,

Source§

fn global(cx: &App) -> &T

Returns the global instance of the implementing type. Read more
Source§

impl<R, P> ReadPrimitive<R> for P
where R: Read + ReadEndian<P>, P: Default,

Source§

fn read_from_little_endian(read: &mut R) -> Result<Self, Error>

Read this value from the supplied reader. Same as ReadEndian::read_from_little_endian().
Source§

fn read_from_big_endian(read: &mut R) -> Result<Self, Error>

Read this value from the supplied reader. Same as ReadEndian::read_from_big_endian().
Source§

fn read_from_native_endian(read: &mut R) -> Result<Self, Error>

Read this value from the supplied reader. Same as ReadEndian::read_from_native_endian().
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<T> UpdateGlobal for T
where T: Global,

Source§

fn update_global<C, F, R>(cx: &mut C, update: F) -> R
where C: BorrowAppContext, F: FnOnce(&mut T, &mut C) -> R,

Updates the global instance of the implementing type using the provided closure. Read more
Source§

fn set_global<C>(cx: &mut C, global: T)

Set the global instance of the implementing type.
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