use std::collections::HashSet;
use std::sync::Mutex;
pub struct SuspensionState {
pub is_suspended: bool,
pub changed_entities: HashSet<String>,
pub suspension_depth: i32,
}
lazy_static::lazy_static! {
static ref SUSPENSION_STATE: Mutex<SuspensionState> = Mutex::new(SuspensionState {
is_suspended: false,
changed_entities: HashSet::new(),
suspension_depth: 0,
});
}
pub fn suspend() -> Result<(), String> {
let mut state = SUSPENSION_STATE.lock().unwrap();
state.suspension_depth += 1;
if state.suspension_depth == 1 {
state.is_suspended = true;
state.changed_entities.clear();
}
Ok(())
}
pub fn resume() -> Result<(), String> {
let mut state = SUSPENSION_STATE.lock().unwrap();
if state.suspension_depth == 0 {
return Err("Cannot resume: not suspended".to_string());
}
state.suspension_depth -= 1;
if state.suspension_depth == 0 {
state.is_suspended = false;
}
Ok(())
}
#[must_use]
pub fn is_suspended() -> bool {
SUSPENSION_STATE.lock().unwrap().is_suspended
}
pub fn record_change(entity_name: &str) {
if is_suspended() {
SUSPENSION_STATE
.lock()
.unwrap()
.changed_entities
.insert(entity_name.to_string());
}
}
#[must_use]
pub fn get_changed_entities() -> Vec<String> {
SUSPENSION_STATE
.lock()
.unwrap()
.changed_entities
.iter()
.cloned()
.collect()
}
pub fn clear_changed_entities() {
SUSPENSION_STATE.lock().unwrap().changed_entities.clear();
}
pub fn catch_up() -> crate::TViewResult<Vec<String>> {
let changed = get_changed_entities();
clear_changed_entities();
if changed.is_empty() {
return Ok(Vec::new());
}
let graph = crate::queue::graph::EntityDepGraph::load()?;
let mut order: Vec<String> = Vec::new();
let mut queue: std::collections::VecDeque<String> = changed.into_iter().collect();
while let Some(entity) = queue.pop_front() {
if order.contains(&entity) {
continue;
}
queue.extend(graph.parents.get(&entity).cloned().unwrap_or_default());
order.push(entity);
}
order.sort_by_key(|e| graph.topo_order.iter().position(|t| t == e));
for entity in &order {
let args = [unsafe {
pgrx::datum::DatumWithOid::new(
entity.as_str(),
pgrx::PgOid::BuiltIn(pgrx::PgBuiltInOids::TEXTOID).value(),
)
}];
let sql = format!(
"SELECT {}.pg_tviews_refresh($1)",
crate::utils::ext_schema()
);
let _owner = crate::owner::AsOwner::of_entity(entity)?;
pgrx::Spi::run_with_args(&sql, &args).map_err(|e| crate::TViewError::SpiError {
query: format!("pg_tviews_refresh('{entity}')"),
error: e.to_string(),
})?;
}
Ok(order)
}
pub fn force_resume() {
let mut state = SUSPENSION_STATE.lock().unwrap();
state.suspension_depth = 0;
state.is_suspended = false;
}