use std::sync::Arc;
use std::time::Duration;
use tokio::task::JoinHandle;
use crate::storage::HashtreeStore;
pub const BACKGROUND_EVICTION_INTERVAL: Duration = Duration::from_secs(300);
pub fn spawn_background_eviction_task(
store: Arc<HashtreeStore>,
interval: Duration,
context: &'static str,
) -> JoinHandle<()> {
tokio::spawn(async move {
let mut ticker = tokio::time::interval(interval);
loop {
ticker.tick().await;
run_background_eviction_pass(store.as_ref(), context);
}
})
}
fn run_background_eviction_pass(store: &HashtreeStore, context: &str) {
match store.evict_if_needed() {
Ok(freed) => {
if freed > 0 {
tracing::info!("{} background eviction freed {} bytes", context, freed);
}
}
Err(err) => {
tracing::warn!("{} background eviction error: {}", context, err);
}
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use std::time::Duration;
use hashtree_core::from_hex;
use tempfile::TempDir;
use super::spawn_background_eviction_task;
use crate::storage::{HashtreeStore, PRIORITY_OTHER};
#[tokio::test]
async fn background_eviction_task_evicts_over_limit_tree() {
let temp_dir = TempDir::new().expect("temp dir");
let store = Arc::new(
HashtreeStore::with_options(temp_dir.path(), None, 512).expect("create store"),
);
let hash_hex = store.put_blob(&vec![7u8; 1024]).expect("put blob");
let hash = from_hex(&hash_hex).expect("decode hash");
store
.index_tree(&hash, "owner", Some("tree"), PRIORITY_OTHER, None)
.expect("index tree");
assert!(store.get_tree_meta(&hash).expect("read meta").is_some());
let handle =
spawn_background_eviction_task(Arc::clone(&store), Duration::from_millis(10), "test");
tokio::time::timeout(Duration::from_secs(1), async {
loop {
if store.get_tree_meta(&hash).expect("read meta").is_none() {
break;
}
tokio::time::sleep(Duration::from_millis(10)).await;
}
})
.await
.expect("background eviction timed out");
handle.abort();
}
}