Skip to main content

hashtree_cli/
eviction.rs

1use std::sync::Arc;
2use std::time::Duration;
3
4use tokio::task::JoinHandle;
5
6use crate::storage::HashtreeStore;
7
8pub const BACKGROUND_EVICTION_INTERVAL: Duration = Duration::from_secs(300);
9
10pub fn spawn_background_eviction_task(
11    store: Arc<HashtreeStore>,
12    interval: Duration,
13    context: &'static str,
14) -> JoinHandle<()> {
15    tokio::spawn(async move {
16        let mut ticker = tokio::time::interval(interval);
17        loop {
18            ticker.tick().await;
19            run_background_eviction_pass(store.as_ref(), context);
20        }
21    })
22}
23
24fn run_background_eviction_pass(store: &HashtreeStore, context: &str) {
25    match store.evict_if_needed() {
26        Ok(freed) => {
27            if freed > 0 {
28                tracing::info!("{} background eviction freed {} bytes", context, freed);
29            }
30        }
31        Err(err) => {
32            tracing::warn!("{} background eviction error: {}", context, err);
33        }
34    }
35}
36
37#[cfg(test)]
38mod tests {
39    use std::sync::Arc;
40    use std::time::Duration;
41
42    use hashtree_config::StorageBackend;
43    use hashtree_core::from_hex;
44    use tempfile::TempDir;
45
46    use super::spawn_background_eviction_task;
47    use crate::storage::{HashtreeStore, PRIORITY_OTHER};
48
49    #[tokio::test]
50    async fn background_eviction_task_evicts_over_limit_tree() {
51        let temp_dir = TempDir::new().expect("temp dir");
52        let store = Arc::new(
53            HashtreeStore::with_options_and_backend(
54                temp_dir.path(),
55                None,
56                512,
57                true,
58                &StorageBackend::Fs,
59            )
60            .expect("create store"),
61        );
62
63        let hash_hex = store.put_blob(&vec![7u8; 1024]).expect("put blob");
64        let hash = from_hex(&hash_hex).expect("decode hash");
65
66        store
67            .index_tree(&hash, "owner", Some("tree"), PRIORITY_OTHER, None)
68            .expect("index tree");
69        assert!(store.get_tree_meta(&hash).expect("read meta").is_some());
70
71        let handle =
72            spawn_background_eviction_task(Arc::clone(&store), Duration::from_millis(10), "test");
73
74        tokio::time::timeout(Duration::from_secs(1), async {
75            loop {
76                if store.get_tree_meta(&hash).expect("read meta").is_none() {
77                    break;
78                }
79                tokio::time::sleep(Duration::from_millis(10)).await;
80            }
81        })
82        .await
83        .expect("background eviction timed out");
84
85        handle.abort();
86    }
87}