use std::sync::{Arc, OnceLock, Weak};
use haematite::EventStore;
use haematite::db::{ReclaimStatus, ShardReclaimAnswer};
#[derive(Debug, Clone, Default)]
pub struct SharedReclaimSpine {
spine: Arc<OnceLock<Weak<EventStore>>>,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct QuiesceAnswer {
pub quiesced: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub refused: Option<String>,
pub passes_run: u64,
pub status: ReclaimStatus,
}
impl SharedReclaimSpine {
pub fn install(&self, spine: &Arc<EventStore>) -> bool {
self.spine.set(Arc::downgrade(spine)).is_ok()
}
fn spine(&self) -> Option<Arc<EventStore>> {
self.spine.get().and_then(Weak::upgrade)
}
#[must_use]
pub fn status(&self) -> Option<ReclaimStatus> {
self.spine().map(|spine| spine.database().reclaim_status())
}
#[must_use]
pub fn quiesce(&self) -> Option<QuiesceAnswer> {
let spine = self.spine()?;
let database = spine.database();
let before = passes_finished(&database.reclaim_status());
let (quiesced, refused) = match database.quiesce_reclaim() {
Ok(()) => (true, None),
Err(error) => (false, Some(error.to_string())),
};
let status = database.reclaim_status();
Some(QuiesceAnswer {
quiesced,
refused,
passes_run: passes_finished(&status).saturating_sub(before),
status,
})
}
}
fn passes_finished(status: &ReclaimStatus) -> u64 {
status
.shards
.iter()
.map(|answer| match answer {
ShardReclaimAnswer::Answered { status, .. } => status.passes_finished,
ShardReclaimAnswer::NotAnswering { .. } => 0,
})
.sum()
}