1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use crate::dhashmap::DHashMap;
use parking_lot::Mutex;
use std::hash::Hash;
use std::time;
pub const VALID_DURATION: time::Duration = time::Duration::from_secs(6 * 60 * 60);
pub const VALID_CHECK_INTERVAL: time::Duration = time::Duration::from_secs(30 * 60);
pub const SAVE_INTERVAL: time::Duration = time::Duration::from_secs(3 * 60);
pub struct TimedCache<K, V>
where
K: Hash + Eq + Clone,
{
storage: DHashMap<K, (V, time::Instant, bool)>,
load_item_fn: fn(&K) -> Option<V>,
save_item_fn: fn(&K, &V) -> bool,
last_saved: Mutex<time::Instant>,
last_purged: Mutex<time::Instant>,
valid_duration: time::Duration,
valid_check_interval: time::Duration,
save_interval: time::Duration,
}
impl<'a, K: Hash + Eq + Clone, V> TimedCache<K, V> {
pub fn new(
load_item: fn(&K) -> Option<V>,
save_item: fn(&K, &V) -> bool,
valid_duration: Option<time::Duration>,
valid_check_interval: Option<time::Duration>,
save_interval: Option<time::Duration>,
) -> Self {
Self {
storage: DHashMap::default(),
load_item_fn: load_item,
save_item_fn: save_item,
last_saved: Mutex::new(time::Instant::now()),
last_purged: Mutex::new(time::Instant::now()),
valid_duration: valid_duration.unwrap_or(VALID_DURATION),
valid_check_interval: valid_check_interval.unwrap_or(VALID_CHECK_INTERVAL),
save_interval: save_interval.unwrap_or(SAVE_INTERVAL),
}
}
pub fn load_item(&self, k: &K) {
if !self.storage.contains_key(k) {
if let Some(v) = (self.load_item_fn)(k) {
let v = (v, time::Instant::now(), true);
self.storage.insert(k.clone(), v);
}
}
}
pub fn map<T, F: FnOnce(&V) -> T>(&self, k: &K, f: F) -> T {
self.load_item(k);
let data = self.storage.get(k).unwrap();
f(&data.0)
}
pub fn map_mut<T, F: FnOnce(&mut V) -> T>(&self, k: &K, f: F) -> T {
self.load_item(k);
let mut data = self.storage.get_mut(k).unwrap();
data.2 = false;
f(&mut data.0)
}
pub fn save_all(&self) {
let check_save_item = |k: &K, v: &mut (V, time::Instant, bool)| {
if !v.2 && (self.save_item_fn)(k, &v.0) {
v.2 = true;
}
};
self.storage.chunks_write().for_each(|mut submap| {
submap
.iter_mut()
.for_each(|(k, mut v)| check_save_item(&k, &mut v))
});
}
pub fn do_check(&self) {
let now = time::Instant::now();
let mut last_saved = self.last_saved.lock();
let mut last_purged = self.last_purged.lock();
let check_save_item = |k: &K, v: &mut (V, time::Instant, bool)| {
if !v.2 && (self.save_item_fn)(k, &v.0) {
v.2 = true;
}
};
let check_to_evict = |_k: &K, v: &mut (V, time::Instant, bool)| -> bool {
now.duration_since(v.1) > self.valid_duration && v.2
};
if now.duration_since(*last_saved) > self.save_interval {
*last_saved = now;
self.storage.chunks_write().for_each(|mut submap| {
submap
.iter_mut()
.for_each(|(k, mut v)| check_save_item(&k, &mut v))
});
}
if now.duration_since(*last_purged) > self.valid_check_interval {
*last_purged = now;
self.storage.retain(|k, v| !check_to_evict(k, v));
}
}
}