use std::sync::Arc;
use crate::stores::ServerStores;
pub const EVERY: std::time::Duration = std::time::Duration::from_secs(6 * 3600);
pub fn sweep_once(stores: &ServerStores, now: i64) -> u64 {
if stores.is_twin() {
return 0;
}
let auth = stores.auth();
let Ok(principals) = auth.principals() else {
return 0;
};
let mut dropped = 0;
for p in principals {
if p.kind != gunnar_core::PrincipalKind::User || !matches!(auth.employer_of(&p.id), Ok(None)) {
continue;
}
match stores.account_audit().prune(&p.id, now) {
Ok(n) => dropped += n,
Err(err) => tracing::warn!(
target: "gunnar::audit",
event = "account_audit.sweep_failed",
account = %p.id,
error = %err,
"an account's audit log could not be swept"
),
}
}
if dropped > 0 {
tracing::info!(
target: "gunnar::audit",
event = "account_audit.swept",
dropped,
"account audit entries past their retention were dropped"
);
}
dropped
}
pub const SWEEP_JOB: &str = "account-audit-sweep";
pub fn start(stores: &Arc<ServerStores>, every: std::time::Duration) -> Result<Arc<gunnar_common::jobs::Job>, String> {
let weak = Arc::downgrade(stores);
let say: gunnar_common::jobs::Say = Arc::new(|l: &str| tracing::error!(target: "gunnar::audit", "{l}"));
gunnar_common::jobs::start(SWEEP_JOB, every, say, move || match weak.upgrade() {
Some(stores) => {
sweep_once(&stores, gunnar_core::now_ms());
gunnar_common::jobs::Round::Again
}
None => gunnar_common::jobs::Round::Done,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_sweep_is_a_jera_job_that_ends_with_the_stores() {
let dir = tempfile::tempdir().expect("tempdir");
let stores = Arc::new(ServerStores::open(dir.path()).expect("stores"));
let job = start(&stores, std::time::Duration::from_millis(10)).expect("the sweep starts as a jera job");
assert_eq!(job.kind(), SWEEP_JOB);
let t = std::time::Instant::now();
while job.queue().stats().done < 2 && t.elapsed() < std::time::Duration::from_secs(10) {
std::thread::sleep(std::time::Duration::from_millis(10));
}
assert!(job.queue().stats().done >= 2, "the passes are done jera jobs: {:?}", job.queue().stats());
drop(stores);
let t = std::time::Instant::now();
while !job.is_stopped() && t.elapsed() < std::time::Duration::from_secs(10) {
std::thread::sleep(std::time::Duration::from_millis(10));
}
assert!(job.is_stopped(), "the job ended with the stores");
}
}