use std::{
collections::BTreeSet,
sync::{Arc, Mutex},
};
use fraiseql_db::{ChangedRows, ViewName};
use super::{
CascadeInvalidator, QueryResultCache, adapter::CachedDatabaseAdapter, tviews_feed::Collection,
};
use crate::{db::DatabaseAdapter, error::Result};
impl<A: DatabaseAdapter> CachedDatabaseAdapter<A> {
pub fn invalidate_views(&self, views: &[ViewName]) -> Result<()> {
invalidate_expanded(&self.cache, self.cascade_invalidator.as_ref(), views)
}
pub fn invalidate_by_entity(&self, entity_type: &str, entity_id: &str) -> Result<u64> {
self.cache.invalidate_by_entity(entity_type, entity_id)
}
}
pub(super) fn invalidate_expanded(
cache: &QueryResultCache,
cascade: Option<&Arc<Mutex<CascadeInvalidator>>>,
views: &[ViewName],
) -> Result<()> {
if !cache.is_enabled() {
return Ok(());
}
cache.invalidate_views(&expand(cascade, views)?)
}
pub(super) fn invalidate_written(
cache: &QueryResultCache,
cascade: Option<&Arc<Mutex<CascadeInvalidator>>>,
views: &[ViewName],
changed: &ChangedRows,
) -> Result<()> {
if !cache.is_enabled() {
return Ok(());
}
let views = expand(cascade, views)?;
let collections: Option<Vec<(Collection, &BTreeSet<String>)>> =
changed.collections.as_ref().and_then(|collections| {
collections
.iter()
.map(|(key, parents)| {
let (child_type, column) = key.split_once('.')?;
Some((Collection::new(child_type, column), parents))
})
.collect()
});
match collections {
Some(_) => cache.invalidate_views_except_collections(&views)?,
None => cache.invalidate_views_except_by_id(&views)?,
}
for (type_name, ids) in &changed.rows {
for id in ids {
cache.invalidate_by_id(type_name, id)?;
}
}
for type_name in &changed.types {
cache.invalidate_by_id_type(type_name)?;
}
if let Some(collections) = collections {
for (collection, parents) in collections {
for parent in parents {
cache.invalidate_collection(&collection, parent)?;
}
}
for type_name in &changed.types {
cache.invalidate_collections_of(type_name)?;
}
}
Ok(())
}
fn expand(
cascade: Option<&Arc<Mutex<CascadeInvalidator>>>,
views: &[ViewName],
) -> Result<Vec<ViewName>> {
let Some(cascader) = cascade else {
return Ok(views.to_vec());
};
let mut expanded: std::collections::HashSet<String> =
views.iter().map(|v| v.as_str().to_owned()).collect();
let mut guard = cascader.lock().map_err(|e| crate::error::FraiseQLError::Internal {
message: format!("Cascade invalidator lock poisoned: {e}"),
source: None,
})?;
for view in views {
let transitive = guard.cascade_invalidate(view.as_str())?;
expanded.extend(transitive);
}
Ok(expanded.into_iter().map(ViewName::from).collect())
}