pub struct CachedDatabaseAdapter<A: DatabaseAdapter> { /* private fields */ }Expand description
Cached database adapter wrapper.
Wraps any DatabaseAdapter implementation with transparent query result caching.
Cache keys include query, variables, WHERE clause, and schema version for security
and correctness.
§Cache Behavior
- Cache Hit: Returns cached result in ~0.1ms (50-200x faster than database)
- Cache Miss: Executes query via underlying adapter, stores result in cache
- Invalidation: Call
invalidate_views()after mutations to clear affected caches
§Thread Safety
This adapter is Send + Sync and can be safely shared across async tasks.
The underlying cache uses Arc<Mutex<>> for thread-safe access.
§Example
use fraiseql_core::cache::{CachedDatabaseAdapter, QueryResultCache, CacheConfig, InvalidationContext};
use fraiseql_core::db::{postgres::PostgresAdapter, DatabaseAdapter};
let db = PostgresAdapter::new("postgresql://localhost/db").await?;
let cache = QueryResultCache::new(CacheConfig::default());
let adapter = CachedDatabaseAdapter::new(db, cache, "1.0.0".to_string());
// First query - cache miss (slower)
let users1 = adapter.execute_where_query("v_user", Some(&where_clause), None, None).await?;
// Second query - cache hit (fast!)
let users2 = adapter.execute_where_query("v_user", Some(&where_clause), None, None).await?;
// After mutation, invalidate
let invalidation = InvalidationContext::for_mutation(
"createUser",
vec!["v_user".to_string()]
);
adapter.invalidate_views(&invalidation.modified_views)?;Implementations§
Source§impl<A: DatabaseAdapter> CachedDatabaseAdapter<A>
impl<A: DatabaseAdapter> CachedDatabaseAdapter<A>
Sourcepub fn new(adapter: A, cache: QueryResultCache, schema_version: String) -> Self
pub fn new(adapter: A, cache: QueryResultCache, schema_version: String) -> Self
Create new cached database adapter.
§Arguments
adapter- Underlying database adapter to wrapcache- Query result cache instanceschema_version- Current schema version (e.g., git hash, semver)
§Example
use fraiseql_core::cache::{CachedDatabaseAdapter, QueryResultCache, CacheConfig};
use fraiseql_core::db::postgres::PostgresAdapter;
let db = PostgresAdapter::new("postgresql://localhost/db").await?;
let cache = QueryResultCache::new(CacheConfig::default());
let adapter = CachedDatabaseAdapter::new(
db,
cache,
env!("CARGO_PKG_VERSION").to_string() // Use package version
);Sourcepub fn with_fact_table_config(
adapter: A,
cache: QueryResultCache,
schema_version: String,
fact_table_config: FactTableCacheConfig,
) -> Self
pub fn with_fact_table_config( adapter: A, cache: QueryResultCache, schema_version: String, fact_table_config: FactTableCacheConfig, ) -> Self
Create new cached database adapter with fact table caching configuration.
§Arguments
adapter- Underlying database adapter to wrapcache- Query result cache instanceschema_version- Current schema version (e.g., git hash, semver)fact_table_config- Configuration for fact table aggregation caching
§Example
use fraiseql_core::cache::{
CachedDatabaseAdapter, QueryResultCache, CacheConfig,
FactTableCacheConfig, FactTableVersionStrategy,
};
use fraiseql_core::db::postgres::PostgresAdapter;
let db = PostgresAdapter::new("postgresql://localhost/db").await?;
let cache = QueryResultCache::new(CacheConfig::default());
// Configure fact table caching strategies
let mut ft_config = FactTableCacheConfig::default();
ft_config.set_strategy("tf_sales", FactTableVersionStrategy::VersionTable);
ft_config.set_strategy("tf_events", FactTableVersionStrategy::time_based(300));
let adapter = CachedDatabaseAdapter::with_fact_table_config(
db,
cache,
"1.0.0".to_string(),
ft_config,
);Sourcepub fn invalidate_views(&self, views: &[String]) -> Result<u64>
pub fn invalidate_views(&self, views: &[String]) -> Result<u64>
Invalidate cache entries that read from specified views.
Call this after mutations to ensure cache consistency. All cache entries that accessed any of the modified views will be removed.
§Arguments
views- List of views/tables that were modified
§Returns
Number of cache entries invalidated
§Errors
Returns error if cache mutex is poisoned (very rare).
§Example
// After creating a user
let count = adapter.invalidate_views(&["v_user".to_string()])?;
println!("Invalidated {} cache entries", count);Sourcepub fn invalidate_cascade_entities(
&self,
cascade_response: &Value,
parser: &CascadeResponseParser,
) -> Result<u64>
pub fn invalidate_cascade_entities( &self, cascade_response: &Value, parser: &CascadeResponseParser, ) -> Result<u64>
Invalidate cache entries based on GraphQL Cascade response entities.
This is the entity-aware invalidation method that provides more precise invalidation. Instead of invalidating all caches reading from a view, only caches that depend on the affected entities are invalidated.
§Arguments
cascade_response- GraphQL mutation response with cascade fieldparser- CascadeResponseParser to extract entities
§Returns
Number of cache entries invalidated
§Example
let cascade_response = json!({
"createPost": {
"cascade": {
"updated": [
{ "__typename": "User", "id": "uuid-1" }
]
}
}
});
let parser = CascadeResponseParser::new();
let count = adapter.invalidate_cascade_entities(&cascade_response, &parser)?;
println!("Invalidated {} cache entries", count);§Note on Performance
This method replaces view-level invalidation with entity-level invalidation. Instead of clearing all caches that touch a view (e.g., v_user), only caches that touch the specific entities are cleared (e.g., User:uuid-1).
Expected improvement:
- View-level: 60-70% hit rate (many false positives)
- Entity-level: 90-95% hit rate (only true positives)
Sourcepub const fn inner(&self) -> &A
pub const fn inner(&self) -> &A
Get reference to underlying adapter.
Useful for accessing adapter-specific methods not in the DatabaseAdapter trait.
§Example
// Access PostgreSQL-specific functionality
let pg_adapter = adapter.inner();Sourcepub fn cache(&self) -> &QueryResultCache
pub fn cache(&self) -> &QueryResultCache
Get reference to cache.
Useful for metrics and monitoring.
§Example
let metrics = adapter.cache().metrics()?;
println!("Cache hit rate: {:.1}%", metrics.hit_rate() * 100.0);Sourcepub fn schema_version(&self) -> &str
pub fn schema_version(&self) -> &str
Sourcepub fn fact_table_config(&self) -> &FactTableCacheConfig
pub fn fact_table_config(&self) -> &FactTableCacheConfig
Get fact table cache configuration.
Sourcepub fn version_provider(&self) -> &FactTableVersionProvider
pub fn version_provider(&self) -> &FactTableVersionProvider
Get the version provider for fact tables.
Sourcepub async fn execute_aggregation_query(
&self,
sql: &str,
) -> Result<Vec<HashMap<String, Value>>>
pub async fn execute_aggregation_query( &self, sql: &str, ) -> Result<Vec<HashMap<String, Value>>>
Execute aggregation query with caching based on fact table versioning strategy.
This method provides transparent caching for aggregation queries on fact tables. The caching behavior depends on the configured strategy for the fact table.
§Arguments
sql- The aggregation SQL query
§Returns
Query results (from cache or database)
§Example
// This query will be cached according to tf_sales strategy
let results = adapter.execute_aggregation_query(
"SELECT SUM(revenue) FROM tf_sales WHERE year = 2024"
).await?;