pub struct QueryResultCache { /* private fields */ }Expand description
Thread-safe W-TinyLFU cache for query results.
Backed by moka::sync::Cache which provides lock-free reads via
Concurrent TinyLFU. Reverse DashMap indexes enable O(k) invalidation.
§Thread Safety
moka::sync::Cache is Send + Sync. All reverse indexes use DashMap
(fine-grained shard locking) and DashSet (also shard-locked). There is no
global mutex on the read path.
§Example
use fraiseql_core::cache::{QueryResultCache, CacheConfig};
use fraiseql_core::db::types::JsonbValue;
use serde_json::json;
let cache = QueryResultCache::new(CacheConfig::default());
// Cache a result
let result = vec![JsonbValue::new(json!({"id": 1, "name": "Alice"}))];
cache.put(
12345_u64,
result.clone(),
vec!["v_user".to_string()],
None, // use global TTL
None, // no entity type index
).unwrap();
// Retrieve from cache
if let Some(cached) = cache.get(12345).unwrap() {
println!("Cache hit! {} results", cached.len());
}Implementations§
Source§impl QueryResultCache
impl QueryResultCache
Sourcepub fn new(config: CacheConfig) -> Self
pub fn new(config: CacheConfig) -> Self
Sourcepub const fn is_enabled(&self) -> bool
pub const fn is_enabled(&self) -> bool
Returns whether caching is enabled.
Used by CachedDatabaseAdapter to short-circuit key generation
and result clone overhead when caching is disabled.
Sourcepub fn get(&self, cache_key: u64) -> Result<Option<Arc<Vec<JsonbValue>>>>
pub fn get(&self, cache_key: u64) -> Result<Option<Arc<Vec<JsonbValue>>>>
Look up a cached result by its cache key.
Returns None when caching is disabled or the key is not present or expired.
Moka handles TTL expiry internally — if get() returns Some, the entry is live.
§Errors
This method is infallible. The Result return type is kept for API compatibility.
Sourcepub fn put_arc(
&self,
cache_key: u64,
result: Arc<Vec<JsonbValue>>,
accessed_views: Vec<String>,
ttl_override: Option<u64>,
entity_type: Option<&str>,
) -> Result<()>
pub fn put_arc( &self, cache_key: u64, result: Arc<Vec<JsonbValue>>, accessed_views: Vec<String>, ttl_override: Option<u64>, entity_type: Option<&str>, ) -> Result<()>
Store query result in cache, accepting an already-Arc-wrapped result.
Preferred over put on the hot miss path: callers that already
hold an Arc<Vec<JsonbValue>> (e.g. CachedDatabaseAdapter) can store it
without an extra Vec clone.
§Arguments
cache_key- Cache key (fromgenerate_cache_key())result- Arc-wrapped query result to cacheaccessed_views- List of views accessed by this queryttl_override- Per-entry TTL in seconds;NoneusesCacheConfig::ttl_secondsentity_type- Optional GraphQL type name for entity-ID indexing
§Errors
This method is infallible. The Result return type is kept for API compatibility.
Sourcepub fn put(
&self,
cache_key: u64,
result: Vec<JsonbValue>,
accessed_views: Vec<String>,
ttl_override: Option<u64>,
entity_type: Option<&str>,
) -> Result<()>
pub fn put( &self, cache_key: u64, result: Vec<JsonbValue>, accessed_views: Vec<String>, ttl_override: Option<u64>, entity_type: Option<&str>, ) -> Result<()>
Store query result in cache.
If caching is disabled, this is a no-op.
Wraps result in an Arc and delegates to put_arc.
Prefer put_arc when the caller already holds an Arc.
§Arguments
cache_key- Cache key (fromgenerate_cache_key())result- Query result to cacheaccessed_views- List of views accessed by this queryttl_override- Per-entry TTL in seconds;NoneusesCacheConfig::ttl_secondsentity_type- Optional GraphQL type name (e.g."User") for entity-ID indexing. When provided, each row’s"id"field is extracted and stored inentity_indexso thatinvalidate_by_entity()can perform selective eviction.
§Errors
This method is infallible. The Result return type is kept for API compatibility.
§Example
use fraiseql_core::cache::{QueryResultCache, CacheConfig};
use fraiseql_core::db::types::JsonbValue;
use serde_json::json;
let cache = QueryResultCache::new(CacheConfig::default());
let result = vec![JsonbValue::new(json!({"id": "uuid-1"}))];
cache.put(0xabc123, result, vec!["v_user".to_string()], None, Some("User"))?;Sourcepub fn invalidate_views(&self, views: &[ViewName]) -> Result<u64>
pub fn invalidate_views(&self, views: &[ViewName]) -> Result<u64>
Invalidate entries accessing specified views.
Uses the view_index for O(k) lookup instead of O(n) full-cache scan.
Keys accessing multiple views in views are deduplicated before invalidation.
§Arguments
views- List of view/table names modified by mutation
§Returns
Number of cache entries invalidated.
§Errors
This method is infallible. The Result return type is kept for API compatibility.
§Example
use fraiseql_core::cache::{QueryResultCache, CacheConfig};
use fraiseql_db::ViewName;
let cache = QueryResultCache::new(CacheConfig::default());
// After createUser mutation
let invalidated = cache.invalidate_views(&[ViewName::from("v_user")])?;
println!("Invalidated {} cache entries", invalidated);Sourcepub fn invalidate_list_queries(&self, views: &[ViewName]) -> Result<u64>
pub fn invalidate_list_queries(&self, views: &[ViewName]) -> Result<u64>
Evict only list (multi-row) cache entries for the given views.
Unlike invalidate_views(), this method leaves single-entity point-lookup
entries intact. Used for CREATE mutations: creating a new entity does not
affect queries that fetch a different existing entity by UUID, but it
does invalidate queries that return a variable-length list of entities.
Uses the list_index for O(k) lookup.
§Errors
This method is infallible. The Result return type is kept for API compatibility.
Sourcepub fn invalidate_by_entity(
&self,
entity_type: &str,
entity_id: &str,
) -> Result<u64>
pub fn invalidate_by_entity( &self, entity_type: &str, entity_id: &str, ) -> Result<u64>
Evict cache entries that contain a specific entity UUID.
Uses the entity_index for O(k) lookup. Entries not referencing this
entity are left untouched.
§Arguments
entity_type- GraphQL type name (e.g."User")entity_id- UUID string of the mutated entity
§Returns
Number of cache entries evicted.
§Errors
This method is infallible. The Result return type is kept for API compatibility.
Sourcepub fn metrics(&self) -> Result<CacheMetrics>
pub fn metrics(&self) -> Result<CacheMetrics>
Get cache metrics snapshot.
Returns a consistent snapshot of current counters. Individual fields may be updated independently (atomics), so the snapshot is not a single atomic transaction, but is accurate enough for monitoring.
§Errors
This method is infallible. The Result return type is kept for API compatibility.
§Example
use fraiseql_core::cache::{QueryResultCache, CacheConfig};
let cache = QueryResultCache::new(CacheConfig::default());
let metrics = cache.metrics()?;
println!("Hit rate: {:.1}%", metrics.hit_rate() * 100.0);
println!("Size: {} / {} entries", metrics.size, 10_000);Sourcepub fn clear(&self) -> Result<()>
pub fn clear(&self) -> Result<()>
Clear all cache entries.
Resets the store, reverse indexes, and memory_bytes synchronously.
The eviction listener will still fire asynchronously for each evicted entry,
but its index-cleanup operations will be no-ops on the already-cleared maps.
§Errors
This method is infallible. The Result return type is kept for API compatibility.
§Example
use fraiseql_core::cache::{QueryResultCache, CacheConfig};
let cache = QueryResultCache::new(CacheConfig::default());
cache.clear()?;