/build/source/nativelink-util/src/evicting_map.rs
Line | Count | Source |
1 | | // Copyright 2024 The NativeLink Authors. All rights reserved. |
2 | | // |
3 | | // Licensed under the Functional Source License, Version 1.1, Apache 2.0 Future License (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // See LICENSE file for details |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | use core::borrow::Borrow; |
16 | | use core::cmp::Eq; |
17 | | use core::fmt::Debug; |
18 | | use core::future::Future; |
19 | | use core::hash::Hash; |
20 | | use core::ops::RangeBounds; |
21 | | use std::collections::BTreeSet; |
22 | | use std::sync::Arc; |
23 | | |
24 | | use lru::LruCache; |
25 | | use nativelink_config::stores::EvictionPolicy; |
26 | | use nativelink_metric::MetricsComponent; |
27 | | use parking_lot::Mutex; |
28 | | use serde::{Deserialize, Serialize}; |
29 | | use tonic::async_trait; |
30 | | use tracing::{debug, info}; |
31 | | |
32 | | use crate::instant_wrapper::InstantWrapper; |
33 | | use crate::metrics_utils::{Counter, CounterWithTime}; |
34 | | |
35 | | #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)] |
36 | | pub struct SerializedLRU<K> { |
37 | | pub data: Vec<(K, i32)>, |
38 | | pub anchor_time: u64, |
39 | | } |
40 | | |
41 | | #[derive(Debug)] |
42 | | struct EvictionItem<T: LenEntry + Debug> { |
43 | | seconds_since_anchor: i32, |
44 | | data: T, |
45 | | } |
46 | | |
47 | | pub trait LenEntry: 'static { |
48 | | /// Length of referenced data. |
49 | | fn len(&self) -> u64; |
50 | | |
51 | | /// Returns `true` if `self` has zero length. |
52 | | fn is_empty(&self) -> bool; |
53 | | |
54 | | /// This will be called when object is removed from map. |
55 | | /// Note: There may still be a reference to it held somewhere else, which |
56 | | /// is why it can't be mutable. This is a good place to mark the item |
57 | | /// to be deleted and then in the Drop call actually do the deleting. |
58 | | /// This will ensure nowhere else in the program still holds a reference |
59 | | /// to this object. |
60 | | /// You should not rely only on the Drop trait. Doing so might result in the |
61 | | /// program safely shutting down and calling the Drop method on each object, |
62 | | /// which if you are deleting items you may not want to do. |
63 | | /// It is undefined behavior to have `unref()` called more than once. |
64 | | /// During the execution of `unref()` no items can be added or removed to/from |
65 | | /// the `EvictionMap` globally (including inside `unref()`). |
66 | | #[inline] |
67 | 46 | fn unref(&self) -> impl Future<Output = ()> + Send { |
68 | 46 | core::future::ready(()) |
69 | 46 | } |
70 | | } |
71 | | |
72 | | impl<T: LenEntry + Send + Sync> LenEntry for Arc<T> { |
73 | | #[inline] |
74 | 281 | fn len(&self) -> u64 { |
75 | 281 | T::len(self.as_ref()) |
76 | 281 | } |
77 | | |
78 | | #[inline] |
79 | 0 | fn is_empty(&self) -> bool { |
80 | 0 | T::is_empty(self.as_ref()) |
81 | 0 | } |
82 | | |
83 | | #[inline] |
84 | 28 | async fn unref(&self) { |
85 | 28 | self.as_ref().unref().await; |
86 | 28 | } |
87 | | } |
88 | | |
89 | | // Callback to be called when the EvictingMap removes an item |
90 | | // either via eviction or direct deletion. This will be called with |
91 | | // whatever key type the EvictingMap uses. |
92 | | #[async_trait] |
93 | | pub trait RemoveStateCallback<Q>: Debug + Send + Sync { |
94 | | async fn callback(&self, key: &Q); |
95 | | } |
96 | | |
97 | | #[derive(Debug, MetricsComponent)] |
98 | | struct State< |
99 | | K: Ord + Hash + Eq + Clone + Debug + Send + Borrow<Q>, |
100 | | Q: Ord + Hash + Eq + Debug, |
101 | | T: LenEntry + Debug + Send, |
102 | | > { |
103 | | lru: LruCache<K, EvictionItem<T>>, |
104 | | btree: Option<BTreeSet<K>>, |
105 | | #[metric(help = "Total size of all items in the store")] |
106 | | sum_store_size: u64, |
107 | | |
108 | | #[metric(help = "Number of bytes evicted from the store")] |
109 | | evicted_bytes: Counter, |
110 | | #[metric(help = "Number of items evicted from the store")] |
111 | | evicted_items: CounterWithTime, |
112 | | #[metric(help = "Number of bytes replaced in the store")] |
113 | | replaced_bytes: Counter, |
114 | | #[metric(help = "Number of items replaced in the store")] |
115 | | replaced_items: CounterWithTime, |
116 | | #[metric(help = "Number of bytes inserted into the store since it was created")] |
117 | | lifetime_inserted_bytes: Counter, |
118 | | |
119 | | remove_callbacks: Arc<Mutex<Vec<Box<dyn RemoveStateCallback<Q>>>>>, |
120 | | } |
121 | | |
122 | | impl< |
123 | | K: Ord + Hash + Eq + Clone + Debug + Send + Sync + Borrow<Q>, |
124 | | Q: Ord + Hash + Eq + Debug + Sync, |
125 | | T: LenEntry + Debug + Sync + Send, |
126 | | > State<K, Q, T> |
127 | | { |
128 | | /// Removes an item from the cache and returns the data for deferred cleanup. |
129 | | /// The caller is responsible for calling `unref()` on the returned data outside of the lock. |
130 | | #[must_use] |
131 | 73 | async fn remove(&mut self, key: &Q, eviction_item: &EvictionItem<T>, replaced: bool) -> T |
132 | 73 | where |
133 | 73 | T: Clone, |
134 | 73 | { |
135 | 73 | if let Some(btree0 ) = &mut self.btree { Branch (135:16): [True: 0, False: 0]
Branch (135:16): [True: 0, False: 20]
Branch (135:16): [True: 0, False: 0]
Branch (135:16): [True: 0, False: 0]
Branch (135:16): [Folded - Ignored]
Branch (135:16): [True: 0, False: 6]
Branch (135:16): [True: 0, False: 1]
Branch (135:16): [True: 0, False: 1]
Branch (135:16): [True: 0, False: 14]
Branch (135:16): [True: 0, False: 0]
Branch (135:16): [True: 0, False: 1]
Branch (135:16): [True: 0, False: 3]
Branch (135:16): [True: 0, False: 1]
Branch (135:16): [True: 0, False: 1]
Branch (135:16): [True: 0, False: 1]
Branch (135:16): [True: 0, False: 1]
Branch (135:16): [True: 0, False: 1]
Branch (135:16): [True: 0, False: 5]
Branch (135:16): [True: 0, False: 0]
Branch (135:16): [True: 0, False: 0]
Branch (135:16): [True: 0, False: 0]
Branch (135:16): [Folded - Ignored]
Branch (135:16): [True: 0, False: 16]
Branch (135:16): [True: 0, False: 1]
|
136 | 0 | btree.remove(key.borrow()); |
137 | 73 | } |
138 | 73 | self.sum_store_size -= eviction_item.data.len(); |
139 | 73 | if replaced { Branch (139:12): [True: 0, False: 0]
Branch (139:12): [True: 18, False: 2]
Branch (139:12): [True: 0, False: 0]
Branch (139:12): [True: 0, False: 0]
Branch (139:12): [Folded - Ignored]
Branch (139:12): [True: 0, False: 6]
Branch (139:12): [True: 0, False: 1]
Branch (139:12): [True: 1, False: 0]
Branch (139:12): [True: 0, False: 14]
Branch (139:12): [True: 0, False: 0]
Branch (139:12): [True: 0, False: 1]
Branch (139:12): [True: 0, False: 3]
Branch (139:12): [True: 0, False: 1]
Branch (139:12): [True: 0, False: 1]
Branch (139:12): [True: 1, False: 0]
Branch (139:12): [True: 1, False: 0]
Branch (139:12): [True: 0, False: 1]
Branch (139:12): [True: 3, False: 2]
Branch (139:12): [True: 0, False: 0]
Branch (139:12): [True: 0, False: 0]
Branch (139:12): [True: 0, False: 0]
Branch (139:12): [Folded - Ignored]
Branch (139:12): [True: 16, False: 0]
Branch (139:12): [True: 0, False: 1]
|
140 | 40 | self.replaced_items.inc(); |
141 | 40 | self.replaced_bytes.add(eviction_item.data.len()); |
142 | 40 | } else { |
143 | 33 | self.evicted_items.inc(); |
144 | 33 | self.evicted_bytes.add(eviction_item.data.len()); |
145 | 33 | } |
146 | | |
147 | 73 | let locked_callbacks = self.remove_callbacks.lock_arc(); |
148 | 73 | for callback2 in locked_callbacks.iter() { |
149 | 2 | callback.callback(key).await; |
150 | | } |
151 | | |
152 | | // Return the data for deferred unref outside of lock |
153 | 73 | eviction_item.data.clone() |
154 | 73 | } |
155 | | |
156 | | /// Inserts a new item into the cache. If the key already exists, the old item is returned |
157 | | /// for deferred cleanup. |
158 | | #[must_use] |
159 | 5.94k | async fn put(&mut self, key: &K, eviction_item: EvictionItem<T>) -> Option<T> |
160 | 5.94k | where |
161 | 5.94k | K: Clone, |
162 | 5.94k | T: Clone, |
163 | 5.94k | { |
164 | | // If we are maintaining a btree index, we need to update it. |
165 | 5.94k | if let Some(btree0 ) = &mut self.btree { Branch (165:16): [True: 0, False: 0]
Branch (165:16): [True: 0, False: 5.75k]
Branch (165:16): [True: 0, False: 0]
Branch (165:16): [True: 0, False: 0]
Branch (165:16): [Folded - Ignored]
Branch (165:16): [True: 0, False: 2]
Branch (165:16): [True: 0, False: 27]
Branch (165:16): [True: 0, False: 3]
Branch (165:16): [True: 0, False: 6]
Branch (165:16): [True: 0, False: 2]
Branch (165:16): [True: 0, False: 2]
Branch (165:16): [True: 0, False: 2]
Branch (165:16): [True: 0, False: 2]
Branch (165:16): [True: 0, False: 1]
Branch (165:16): [True: 0, False: 16]
Branch (165:16): [True: 0, False: 0]
Branch (165:16): [True: 0, False: 2]
Branch (165:16): [Folded - Ignored]
Branch (165:16): [True: 0, False: 97]
Branch (165:16): [True: 0, False: 27]
|
166 | 0 | btree.insert(key.clone()); |
167 | 5.94k | } |
168 | 5.94k | if let Some(old_item40 ) = self.lru.put(key.clone(), eviction_item) { Branch (168:16): [True: 0, False: 0]
Branch (168:16): [True: 18, False: 5.73k]
Branch (168:16): [True: 0, False: 0]
Branch (168:16): [True: 0, False: 0]
Branch (168:16): [Folded - Ignored]
Branch (168:16): [True: 1, False: 1]
Branch (168:16): [True: 0, False: 27]
Branch (168:16): [True: 0, False: 3]
Branch (168:16): [True: 0, False: 6]
Branch (168:16): [True: 0, False: 2]
Branch (168:16): [True: 0, False: 2]
Branch (168:16): [True: 1, False: 1]
Branch (168:16): [True: 1, False: 1]
Branch (168:16): [True: 0, False: 1]
Branch (168:16): [True: 3, False: 13]
Branch (168:16): [True: 0, False: 0]
Branch (168:16): [True: 0, False: 2]
Branch (168:16): [Folded - Ignored]
Branch (168:16): [True: 16, False: 81]
Branch (168:16): [True: 0, False: 27]
|
169 | 40 | let old_data = self.remove(key.borrow(), &old_item, true).await; |
170 | 40 | return Some(old_data); |
171 | 5.90k | } |
172 | 5.90k | None |
173 | 5.94k | } |
174 | | |
175 | 5 | fn add_remove_callback(&self, callback: Box<dyn RemoveStateCallback<Q>>) { |
176 | 5 | self.remove_callbacks.lock_arc().push(callback); |
177 | 5 | } |
178 | | } |
179 | | |
180 | | #[derive(Debug, MetricsComponent)] |
181 | | pub struct EvictingMap< |
182 | | K: Ord + Hash + Eq + Clone + Debug + Send + Borrow<Q>, |
183 | | Q: Ord + Hash + Eq + Debug, |
184 | | T: LenEntry + Debug + Send, |
185 | | I: InstantWrapper, |
186 | | > { |
187 | | #[metric] |
188 | | state: Arc<Mutex<State<K, Q, T>>>, |
189 | | anchor_time: I, |
190 | | #[metric(help = "Maximum size of the store in bytes")] |
191 | | max_bytes: u64, |
192 | | #[metric(help = "Number of bytes to evict when the store is full")] |
193 | | evict_bytes: u64, |
194 | | #[metric(help = "Maximum number of seconds to keep an item in the store")] |
195 | | max_seconds: i32, |
196 | | #[metric(help = "Maximum number of items to keep in the store")] |
197 | | max_count: u64, |
198 | | } |
199 | | |
200 | | impl<K, Q, T, I> EvictingMap<K, Q, T, I> |
201 | | where |
202 | | K: Ord + Hash + Eq + Clone + Debug + Send + Sync + Borrow<Q>, |
203 | | Q: Ord + Hash + Eq + Debug + Sync, |
204 | | T: LenEntry + Debug + Clone + Send + Sync, |
205 | | I: InstantWrapper, |
206 | | { |
207 | 406 | pub fn new(config: &EvictionPolicy, anchor_time: I) -> Self { |
208 | 406 | Self { |
209 | 406 | // We use unbounded because if we use the bounded version we can't call the delete |
210 | 406 | // function on the LenEntry properly. |
211 | 406 | state: Arc::new(Mutex::new(State { |
212 | 406 | lru: LruCache::unbounded(), |
213 | 406 | btree: None, |
214 | 406 | sum_store_size: 0, |
215 | 406 | evicted_bytes: Counter::default(), |
216 | 406 | evicted_items: CounterWithTime::default(), |
217 | 406 | replaced_bytes: Counter::default(), |
218 | 406 | replaced_items: CounterWithTime::default(), |
219 | 406 | lifetime_inserted_bytes: Counter::default(), |
220 | 406 | remove_callbacks: Arc::new(Mutex::new(vec![])), |
221 | 406 | })), |
222 | 406 | anchor_time, |
223 | 406 | max_bytes: config.max_bytes as u64, |
224 | 406 | evict_bytes: config.evict_bytes as u64, |
225 | 406 | max_seconds: config.max_seconds as i32, |
226 | 406 | max_count: config.max_count, |
227 | 406 | } |
228 | 406 | } |
229 | | |
230 | 0 | pub async fn enable_filtering(&self) { |
231 | 0 | let mut state = self.state.lock_arc(); |
232 | 0 | if state.btree.is_none() { Branch (232:12): [Folded - Ignored]
Branch (232:12): [Folded - Ignored]
|
233 | 0 | Self::rebuild_btree_index(&mut state); |
234 | 0 | } |
235 | 0 | } |
236 | | |
237 | 2 | fn rebuild_btree_index(state: &mut State<K, Q, T>) { |
238 | 2 | state.btree = Some(state.lru.iter().map(|(k, _)| k).cloned().collect()); |
239 | 2 | } |
240 | | |
241 | | /// Run the `handler` function on each key-value pair that matches the `prefix_range` |
242 | | /// and return the number of items that were processed. |
243 | | /// The `handler` function should return `true` to continue processing the next item |
244 | | /// or `false` to stop processing. |
245 | 11 | pub async fn range<F>(&self, prefix_range: impl RangeBounds<Q> + Send, mut handler: F) -> u64 |
246 | 11 | where |
247 | 11 | F: FnMut(&K, &T) -> bool + Send, |
248 | 11 | K: Ord, |
249 | 11 | { |
250 | 11 | let mut state = self.state.lock_arc(); |
251 | 11 | let btree = if let Some(ref btree9 ) = state.btree { Branch (251:28): [True: 6, False: 1]
Branch (251:28): [Folded - Ignored]
Branch (251:28): [True: 1, False: 0]
Branch (251:28): [True: 2, False: 0]
Branch (251:28): [True: 0, False: 1]
Branch (251:28): [Folded - Ignored]
|
252 | 9 | btree |
253 | | } else { |
254 | 2 | Self::rebuild_btree_index(&mut state); |
255 | 2 | state.btree.as_ref().unwrap() |
256 | | }; |
257 | 11 | let mut continue_count = 0; |
258 | 22 | for key in btree11 .range11 (prefix_range11 ) { |
259 | 22 | let value = &state.lru.peek(key.borrow()).unwrap().data; |
260 | 22 | let should_continue = handler(key, value); |
261 | 22 | if !should_continue { Branch (261:16): [True: 0, False: 13]
Branch (261:16): [Folded - Ignored]
Branch (261:16): [True: 0, False: 1]
Branch (261:16): [True: 0, False: 5]
Branch (261:16): [True: 0, False: 3]
Branch (261:16): [Folded - Ignored]
|
262 | 0 | break; |
263 | 22 | } |
264 | 22 | continue_count += 1; |
265 | | } |
266 | 11 | continue_count |
267 | 11 | } |
268 | | |
269 | | /// Returns the number of key-value pairs that are currently in the the cache. |
270 | | /// Function is not for production code paths. |
271 | 30 | pub async fn len_for_test(&self) -> usize { |
272 | 30 | self.state.lock_arc().lru.len() |
273 | 30 | } |
274 | | |
275 | 10.6k | fn should_evict( |
276 | 10.6k | &self, |
277 | 10.6k | lru_len: usize, |
278 | 10.6k | peek_entry: &EvictionItem<T>, |
279 | 10.6k | sum_store_size: u64, |
280 | 10.6k | max_bytes: u64, |
281 | 10.6k | ) -> bool { |
282 | 10.6k | let is_over_size = max_bytes != 0 && sum_store_size >= max_bytes211 ; Branch (282:28): [True: 0, False: 0]
Branch (282:28): [True: 0, False: 0]
Branch (282:28): [True: 103, False: 10.1k]
Branch (282:28): [True: 0, False: 0]
Branch (282:28): [Folded - Ignored]
Branch (282:28): [True: 0, False: 6]
Branch (282:28): [True: 0, False: 1]
Branch (282:28): [True: 0, False: 3]
Branch (282:28): [True: 30, False: 33]
Branch (282:28): [True: 0, False: 3]
Branch (282:28): [True: 0, False: 1]
Branch (282:28): [True: 0, False: 2]
Branch (282:28): [True: 3, False: 7]
Branch (282:28): [True: 3, False: 0]
Branch (282:28): [True: 0, False: 4]
Branch (282:28): [True: 0, False: 2]
Branch (282:28): [True: 0, False: 3]
Branch (282:28): [True: 0, False: 2]
Branch (282:28): [True: 1, False: 27]
Branch (282:28): [True: 0, False: 0]
Branch (282:28): [True: 0, False: 4]
Branch (282:28): [True: 0, False: 3]
Branch (282:28): [Folded - Ignored]
Branch (282:28): [True: 71, False: 138]
Branch (282:28): [True: 0, False: 85]
|
283 | | |
284 | 10.6k | let evict_older_than_seconds = |
285 | 10.6k | (self.anchor_time.elapsed().as_secs() as i32) - self.max_seconds; |
286 | 10.6k | let old_item_exists = |
287 | 10.6k | self.max_seconds != 0 && peek_entry.seconds_since_anchor < evict_older_than_seconds115 ; Branch (287:13): [True: 0, False: 0]
Branch (287:13): [True: 0, False: 0]
Branch (287:13): [True: 0, False: 10.2k]
Branch (287:13): [True: 0, False: 0]
Branch (287:13): [Folded - Ignored]
Branch (287:13): [True: 0, False: 6]
Branch (287:13): [True: 0, False: 1]
Branch (287:13): [True: 0, False: 3]
Branch (287:13): [True: 30, False: 33]
Branch (287:13): [True: 0, False: 3]
Branch (287:13): [True: 0, False: 1]
Branch (287:13): [True: 0, False: 2]
Branch (287:13): [True: 0, False: 10]
Branch (287:13): [True: 0, False: 3]
Branch (287:13): [True: 0, False: 4]
Branch (287:13): [True: 0, False: 2]
Branch (287:13): [True: 0, False: 3]
Branch (287:13): [True: 0, False: 2]
Branch (287:13): [True: 0, False: 28]
Branch (287:13): [True: 0, False: 0]
Branch (287:13): [True: 0, False: 4]
Branch (287:13): [True: 0, False: 3]
Branch (287:13): [Folded - Ignored]
Branch (287:13): [True: 0, False: 209]
Branch (287:13): [True: 85, False: 0]
|
288 | | |
289 | 10.6k | let is_over_count = self.max_count != 0 && (lru_len as u64) > self.max_count20 ; Branch (289:29): [True: 0, False: 0]
Branch (289:29): [True: 0, False: 0]
Branch (289:29): [True: 0, False: 10.2k]
Branch (289:29): [True: 0, False: 0]
Branch (289:29): [Folded - Ignored]
Branch (289:29): [True: 0, False: 6]
Branch (289:29): [True: 0, False: 1]
Branch (289:29): [True: 3, False: 0]
Branch (289:29): [True: 8, False: 55]
Branch (289:29): [True: 0, False: 3]
Branch (289:29): [True: 0, False: 1]
Branch (289:29): [True: 0, False: 2]
Branch (289:29): [True: 0, False: 10]
Branch (289:29): [True: 0, False: 3]
Branch (289:29): [True: 4, False: 0]
Branch (289:29): [True: 2, False: 0]
Branch (289:29): [True: 3, False: 0]
Branch (289:29): [True: 0, False: 2]
Branch (289:29): [True: 0, False: 28]
Branch (289:29): [True: 0, False: 0]
Branch (289:29): [True: 0, False: 4]
Branch (289:29): [True: 0, False: 3]
Branch (289:29): [Folded - Ignored]
Branch (289:29): [True: 0, False: 209]
Branch (289:29): [True: 0, False: 85]
|
290 | | |
291 | 10.6k | is_over_size || old_item_exists10.6k || is_over_count10.6k Branch (291:9): [True: 0, False: 0]
Branch (291:25): [True: 0, False: 0]
Branch (291:9): [True: 0, False: 0]
Branch (291:25): [True: 0, False: 0]
Branch (291:9): [True: 2, False: 10.2k]
Branch (291:25): [True: 0, False: 10.2k]
Branch (291:9): [True: 0, False: 0]
Branch (291:25): [True: 0, False: 0]
Branch (291:9): [Folded - Ignored]
Branch (291:25): [Folded - Ignored]
Branch (291:9): [True: 0, False: 6]
Branch (291:25): [True: 0, False: 6]
Branch (291:9): [True: 0, False: 1]
Branch (291:25): [True: 0, False: 1]
Branch (291:9): [True: 0, False: 3]
Branch (291:25): [True: 0, False: 3]
Branch (291:9): [True: 6, False: 57]
Branch (291:25): [True: 9, False: 48]
Branch (291:9): [True: 0, False: 3]
Branch (291:25): [True: 0, False: 3]
Branch (291:9): [True: 0, False: 1]
Branch (291:25): [True: 0, False: 1]
Branch (291:9): [True: 0, False: 2]
Branch (291:25): [True: 0, False: 2]
Branch (291:9): [True: 0, False: 10]
Branch (291:25): [True: 0, False: 10]
Branch (291:9): [True: 1, False: 2]
Branch (291:25): [True: 0, False: 2]
Branch (291:9): [True: 0, False: 4]
Branch (291:25): [True: 0, False: 4]
Branch (291:9): [True: 0, False: 2]
Branch (291:25): [True: 0, False: 2]
Branch (291:9): [True: 0, False: 3]
Branch (291:25): [True: 0, False: 3]
Branch (291:9): [True: 0, False: 2]
Branch (291:25): [True: 0, False: 2]
Branch (291:9): [True: 0, False: 28]
Branch (291:25): [True: 0, False: 28]
Branch (291:9): [True: 0, False: 0]
Branch (291:25): [True: 0, False: 0]
Branch (291:9): [True: 0, False: 4]
Branch (291:25): [True: 0, False: 4]
Branch (291:9): [True: 0, False: 3]
Branch (291:25): [True: 0, False: 3]
Branch (291:9): [Folded - Ignored]
Branch (291:25): [Folded - Ignored]
Branch (291:9): [True: 0, False: 209]
Branch (291:25): [True: 0, False: 209]
Branch (291:9): [True: 0, False: 85]
Branch (291:25): [True: 1, False: 84]
|
292 | 10.6k | } |
293 | | |
294 | | #[must_use] |
295 | 5.95k | async fn evict_items(&self, state: &mut State<K, Q, T>) -> Vec<T> { |
296 | 5.95k | let Some((_, mut peek_entry)) = state.lru.peek_lru() else { Branch (296:13): [True: 0, False: 0]
Branch (296:13): [True: 5.75k, False: 0]
Branch (296:13): [True: 0, False: 0]
Branch (296:13): [True: 0, False: 0]
Branch (296:13): [Folded - Ignored]
Branch (296:13): [True: 6, False: 0]
Branch (296:13): [True: 1, False: 0]
Branch (296:13): [True: 2, False: 0]
Branch (296:13): [True: 31, False: 0]
Branch (296:13): [True: 3, False: 0]
Branch (296:13): [True: 2, False: 0]
Branch (296:13): [True: 1, False: 0]
Branch (296:13): [True: 7, False: 0]
Branch (296:13): [True: 2, False: 0]
Branch (296:13): [True: 2, False: 0]
Branch (296:13): [True: 2, False: 0]
Branch (296:13): [True: 2, False: 0]
Branch (296:13): [True: 2, False: 0]
Branch (296:13): [True: 18, False: 0]
Branch (296:13): [True: 0, False: 0]
Branch (296:13): [True: 0, False: 0]
Branch (296:13): [True: 2, False: 0]
Branch (296:13): [Folded - Ignored]
Branch (296:13): [True: 97, False: 0]
Branch (296:13): [True: 27, False: 0]
|
297 | 0 | return Vec::new(); |
298 | | }; |
299 | | |
300 | 5.95k | let max_bytes = if self.max_bytes != 0 Branch (300:28): [True: 0, False: 0]
Branch (300:28): [True: 8, False: 5.74k]
Branch (300:28): [True: 0, False: 0]
Branch (300:28): [True: 0, False: 0]
Branch (300:28): [Folded - Ignored]
Branch (300:28): [True: 0, False: 6]
Branch (300:28): [True: 0, False: 1]
Branch (300:28): [True: 0, False: 2]
Branch (300:28): [True: 8, False: 23]
Branch (300:28): [True: 0, False: 3]
Branch (300:28): [True: 0, False: 2]
Branch (300:28): [True: 0, False: 1]
Branch (300:28): [True: 0, False: 7]
Branch (300:28): [True: 2, False: 0]
Branch (300:28): [True: 0, False: 2]
Branch (300:28): [True: 0, False: 2]
Branch (300:28): [True: 0, False: 2]
Branch (300:28): [True: 0, False: 2]
Branch (300:28): [True: 0, False: 18]
Branch (300:28): [True: 0, False: 0]
Branch (300:28): [True: 0, False: 0]
Branch (300:28): [True: 0, False: 2]
Branch (300:28): [Folded - Ignored]
Branch (300:28): [True: 0, False: 97]
Branch (300:28): [True: 0, False: 27]
|
301 | 18 | && self.evict_bytes != 0 Branch (301:16): [True: 0, False: 0]
Branch (301:16): [True: 0, False: 8]
Branch (301:16): [True: 0, False: 0]
Branch (301:16): [True: 0, False: 0]
Branch (301:16): [Folded - Ignored]
Branch (301:16): [True: 0, False: 0]
Branch (301:16): [True: 0, False: 0]
Branch (301:16): [True: 0, False: 0]
Branch (301:16): [True: 4, False: 4]
Branch (301:16): [True: 0, False: 0]
Branch (301:16): [True: 0, False: 0]
Branch (301:16): [True: 0, False: 0]
Branch (301:16): [True: 0, False: 0]
Branch (301:16): [True: 0, False: 2]
Branch (301:16): [True: 0, False: 0]
Branch (301:16): [True: 0, False: 0]
Branch (301:16): [True: 0, False: 0]
Branch (301:16): [True: 0, False: 0]
Branch (301:16): [True: 0, False: 0]
Branch (301:16): [True: 0, False: 0]
Branch (301:16): [True: 0, False: 0]
Branch (301:16): [True: 0, False: 0]
Branch (301:16): [Folded - Ignored]
Branch (301:16): [True: 0, False: 0]
Branch (301:16): [True: 0, False: 0]
|
302 | 4 | && self.should_evict( Branch (302:16): [True: 0, False: 0]
Branch (302:16): [True: 0, False: 0]
Branch (302:16): [True: 0, False: 0]
Branch (302:16): [True: 0, False: 0]
Branch (302:16): [Folded - Ignored]
Branch (302:16): [True: 0, False: 0]
Branch (302:16): [True: 0, False: 0]
Branch (302:16): [True: 0, False: 0]
Branch (302:16): [True: 1, False: 3]
Branch (302:16): [True: 0, False: 0]
Branch (302:16): [True: 0, False: 0]
Branch (302:16): [True: 0, False: 0]
Branch (302:16): [True: 0, False: 0]
Branch (302:16): [True: 0, False: 0]
Branch (302:16): [True: 0, False: 0]
Branch (302:16): [True: 0, False: 0]
Branch (302:16): [True: 0, False: 0]
Branch (302:16): [True: 0, False: 0]
Branch (302:16): [True: 0, False: 0]
Branch (302:16): [True: 0, False: 0]
Branch (302:16): [True: 0, False: 0]
Branch (302:16): [True: 0, False: 0]
Branch (302:16): [Folded - Ignored]
Branch (302:16): [True: 0, False: 0]
Branch (302:16): [True: 0, False: 0]
|
303 | 4 | state.lru.len(), |
304 | 4 | peek_entry, |
305 | 4 | state.sum_store_size, |
306 | 4 | self.max_bytes, |
307 | | ) { |
308 | 1 | self.max_bytes.saturating_sub(self.evict_bytes) |
309 | | } else { |
310 | 5.95k | self.max_bytes |
311 | | }; |
312 | | |
313 | 5.95k | let mut items_to_unref = Vec::new(); |
314 | | |
315 | 5.97k | while self.should_evict(state.lru.len(), peek_entry, state.sum_store_size, max_bytes) { Branch (315:15): [True: 0, False: 0]
Branch (315:15): [True: 2, False: 5.75k]
Branch (315:15): [True: 0, False: 0]
Branch (315:15): [True: 0, False: 0]
Branch (315:15): [Folded - Ignored]
Branch (315:15): [True: 0, False: 6]
Branch (315:15): [True: 0, False: 1]
Branch (315:15): [True: 0, False: 2]
Branch (315:15): [True: 12, False: 28]
Branch (315:15): [True: 0, False: 3]
Branch (315:15): [True: 0, False: 2]
Branch (315:15): [True: 0, False: 1]
Branch (315:15): [True: 0, False: 7]
Branch (315:15): [True: 1, False: 2]
Branch (315:15): [True: 1, False: 2]
Branch (315:15): [True: 0, False: 2]
Branch (315:15): [True: 0, False: 2]
Branch (315:15): [True: 0, False: 2]
Branch (315:15): [True: 0, False: 18]
Branch (315:15): [True: 0, False: 0]
Branch (315:15): [True: 0, False: 0]
Branch (315:15): [True: 0, False: 2]
Branch (315:15): [Folded - Ignored]
Branch (315:15): [True: 0, False: 97]
Branch (315:15): [True: 1, False: 27]
|
316 | 17 | let (key, eviction_item) = state |
317 | 17 | .lru |
318 | 17 | .pop_lru() |
319 | 17 | .expect("Tried to peek() then pop() but failed"); |
320 | 17 | debug!(?key, "Evicting",); |
321 | 17 | let data = state.remove(key.borrow(), &eviction_item, false).await; |
322 | 17 | items_to_unref.push(data); |
323 | | |
324 | 17 | peek_entry = if let Some((_, entry13 )) = state.lru.peek_lru() { Branch (324:33): [True: 0, False: 0]
Branch (324:33): [True: 1, False: 1]
Branch (324:33): [True: 0, False: 0]
Branch (324:33): [True: 0, False: 0]
Branch (324:33): [Folded - Ignored]
Branch (324:33): [True: 0, False: 0]
Branch (324:33): [True: 0, False: 0]
Branch (324:33): [True: 0, False: 0]
Branch (324:33): [True: 9, False: 3]
Branch (324:33): [True: 0, False: 0]
Branch (324:33): [True: 0, False: 0]
Branch (324:33): [True: 0, False: 0]
Branch (324:33): [True: 0, False: 0]
Branch (324:33): [True: 1, False: 0]
Branch (324:33): [True: 1, False: 0]
Branch (324:33): [True: 0, False: 0]
Branch (324:33): [True: 0, False: 0]
Branch (324:33): [True: 0, False: 0]
Branch (324:33): [True: 0, False: 0]
Branch (324:33): [True: 0, False: 0]
Branch (324:33): [True: 0, False: 0]
Branch (324:33): [True: 0, False: 0]
Branch (324:33): [Folded - Ignored]
Branch (324:33): [True: 0, False: 0]
Branch (324:33): [True: 1, False: 0]
|
325 | 13 | entry |
326 | | } else { |
327 | 4 | break; |
328 | | }; |
329 | | } |
330 | | |
331 | 5.95k | items_to_unref |
332 | 5.95k | } |
333 | | |
334 | | /// Return the size of a `key`, if not found `None` is returned. |
335 | 25 | pub async fn size_for_key(&self, key: &Q) -> Option<u64> |
336 | 25 | where |
337 | 25 | Q: Sync, |
338 | 25 | { |
339 | 25 | let mut results = [None]; |
340 | 25 | self.sizes_for_keys([key], &mut results[..], false).await; |
341 | 25 | results[0] |
342 | 25 | } |
343 | | |
344 | | /// Return the sizes of a collection of `keys`. Expects `results` collection |
345 | | /// to be provided for storing the resulting key sizes. Each index value in |
346 | | /// `keys` maps directly to the size value for the key in `results`. |
347 | | /// If no key is found in the internal map, `None` is filled in its place. |
348 | | /// If `peek` is set to `true`, the items are not promoted to the front of the |
349 | | /// LRU cache. Note: peek may still evict, but won't promote. |
350 | 298 | pub async fn sizes_for_keys<It, R>(&self, keys: It, results: &mut [Option<u64>], peek: bool) |
351 | 298 | where |
352 | 298 | It: IntoIterator<Item = R> + Send, |
353 | 298 | // Note: It's not enough to have the inserts themselves be Send. The |
354 | 298 | // returned iterator should be Send as well. |
355 | 298 | <It as IntoIterator>::IntoIter: Send, |
356 | 298 | // This may look strange, but what we are doing is saying: |
357 | 298 | // * `K` must be able to borrow `Q` |
358 | 298 | // * `R` (the input stream item type) must also be able to borrow `Q` |
359 | 298 | // Note: That K and R do not need to be the same type, they just both need |
360 | 298 | // to be able to borrow a `Q`. |
361 | 298 | R: Borrow<Q> + Send, |
362 | 298 | { |
363 | 298 | let mut state = self.state.lock_arc(); |
364 | | |
365 | 298 | let lru_len = state.lru.len(); |
366 | 333 | for (key, result) in keys298 .into_iter298 ().zip298 (results298 .iter_mut298 ()) { |
367 | 333 | let maybe_entry = if peek { Branch (367:34): [True: 0, False: 0]
Branch (367:34): [True: 0, False: 213]
Branch (367:34): [True: 0, False: 0]
Branch (367:34): [Folded - Ignored]
Branch (367:34): [True: 0, False: 25]
Branch (367:34): [True: 2, False: 0]
Branch (367:34): [True: 5, False: 0]
Branch (367:34): [True: 4, False: 0]
Branch (367:34): [True: 0, False: 0]
Branch (367:34): [True: 0, False: 0]
Branch (367:34): [True: 0, False: 0]
Branch (367:34): [True: 0, False: 0]
Branch (367:34): [True: 0, False: 1]
Branch (367:34): [True: 0, False: 3]
Branch (367:34): [True: 0, False: 0]
Branch (367:34): [True: 0, False: 0]
Branch (367:34): [Folded - Ignored]
Branch (367:34): [True: 0, False: 80]
|
368 | 11 | state.lru.peek_mut(key.borrow()) |
369 | | } else { |
370 | 322 | state.lru.get_mut(key.borrow()) |
371 | | }; |
372 | 333 | match maybe_entry { |
373 | 183 | Some(entry) => { |
374 | | // Note: We need to check eviction because the item might be expired |
375 | | // based on the current time. In such case, we remove the item while |
376 | | // we are here. |
377 | 183 | if self.should_evict(lru_len, entry, 0, u64::MAX) { Branch (377:24): [True: 0, False: 0]
Branch (377:24): [True: 0, False: 94]
Branch (377:24): [True: 0, False: 0]
Branch (377:24): [Folded - Ignored]
Branch (377:24): [True: 1, False: 13]
Branch (377:24): [True: 0, False: 0]
Branch (377:24): [True: 0, False: 0]
Branch (377:24): [True: 0, False: 3]
Branch (377:24): [True: 0, False: 0]
Branch (377:24): [True: 0, False: 0]
Branch (377:24): [True: 0, False: 0]
Branch (377:24): [True: 0, False: 0]
Branch (377:24): [True: 0, False: 0]
Branch (377:24): [True: 0, False: 1]
Branch (377:24): [True: 0, False: 0]
Branch (377:24): [True: 0, False: 0]
Branch (377:24): [Folded - Ignored]
Branch (377:24): [True: 0, False: 71]
|
378 | 1 | *result = None; |
379 | 1 | if let Some((key, eviction_item)) = state.lru.pop_entry(key.borrow()) { Branch (379:32): [True: 0, False: 0]
Branch (379:32): [True: 0, False: 0]
Branch (379:32): [True: 0, False: 0]
Branch (379:32): [Folded - Ignored]
Branch (379:32): [True: 1, False: 0]
Branch (379:32): [True: 0, False: 0]
Branch (379:32): [True: 0, False: 0]
Branch (379:32): [True: 0, False: 0]
Branch (379:32): [True: 0, False: 0]
Branch (379:32): [True: 0, False: 0]
Branch (379:32): [True: 0, False: 0]
Branch (379:32): [True: 0, False: 0]
Branch (379:32): [True: 0, False: 0]
Branch (379:32): [True: 0, False: 0]
Branch (379:32): [True: 0, False: 0]
Branch (379:32): [True: 0, False: 0]
Branch (379:32): [Folded - Ignored]
Branch (379:32): [True: 0, False: 0]
|
380 | 1 | info!(?key, "Item expired, evicting"); |
381 | 1 | let data = state.remove(key.borrow(), &eviction_item, false).await; |
382 | | // Store data for later unref - we can't drop state here as we're still iterating |
383 | | // The unref will happen after the method completes |
384 | | // For now, we just do inline unref |
385 | 1 | data.unref().await; |
386 | 0 | } |
387 | | } else { |
388 | 182 | if !peek { Branch (388:28): [True: 0, False: 0]
Branch (388:28): [True: 94, False: 0]
Branch (388:28): [True: 0, False: 0]
Branch (388:28): [Folded - Ignored]
Branch (388:28): [True: 13, False: 0]
Branch (388:28): [True: 0, False: 0]
Branch (388:28): [True: 0, False: 0]
Branch (388:28): [True: 0, False: 3]
Branch (388:28): [True: 0, False: 0]
Branch (388:28): [True: 0, False: 0]
Branch (388:28): [True: 0, False: 0]
Branch (388:28): [True: 0, False: 0]
Branch (388:28): [True: 0, False: 0]
Branch (388:28): [True: 1, False: 0]
Branch (388:28): [True: 0, False: 0]
Branch (388:28): [True: 0, False: 0]
Branch (388:28): [Folded - Ignored]
Branch (388:28): [True: 71, False: 0]
|
389 | 179 | entry.seconds_since_anchor = |
390 | 179 | self.anchor_time.elapsed().as_secs() as i32; |
391 | 179 | }3 |
392 | 182 | *result = Some(entry.data.len()); |
393 | | } |
394 | | } |
395 | 150 | None => *result = None, |
396 | | } |
397 | | } |
398 | 298 | } |
399 | | |
400 | 4.49k | pub async fn get(&self, key: &Q) -> Option<T> { |
401 | | // Fast path: Check if we need eviction before acquiring lock for eviction |
402 | 4.49k | let needs_eviction = { |
403 | 4.49k | let state = self.state.lock_arc(); |
404 | 4.49k | if let Some((_, peek_entry4.48k )) = state.lru.peek_lru() { Branch (404:20): [True: 0, False: 0]
Branch (404:20): [True: 0, False: 0]
Branch (404:20): [True: 4.36k, False: 4]
Branch (404:20): [Folded - Ignored]
Branch (404:20): [True: 1, False: 0]
Branch (404:20): [True: 5, False: 0]
Branch (404:20): [True: 0, False: 0]
Branch (404:20): [True: 1, False: 0]
Branch (404:20): [True: 0, False: 0]
Branch (404:20): [True: 1, False: 0]
Branch (404:20): [True: 0, False: 0]
Branch (404:20): [True: 9, False: 0]
Branch (404:20): [True: 0, False: 0]
Branch (404:20): [True: 4, False: 0]
Branch (404:20): [True: 1, False: 1]
Branch (404:20): [Folded - Ignored]
Branch (404:20): [True: 41, False: 0]
Branch (404:20): [True: 57, False: 0]
|
405 | 4.48k | self.should_evict( |
406 | 4.48k | state.lru.len(), |
407 | 4.48k | peek_entry, |
408 | 4.48k | state.sum_store_size, |
409 | 4.48k | self.max_bytes, |
410 | | ) |
411 | | } else { |
412 | 5 | false |
413 | | } |
414 | | }; |
415 | | |
416 | | // Perform eviction if needed |
417 | 4.49k | if needs_eviction { Branch (417:12): [True: 0, False: 0]
Branch (417:12): [True: 0, False: 0]
Branch (417:12): [True: 0, False: 4.37k]
Branch (417:12): [Folded - Ignored]
Branch (417:12): [True: 0, False: 1]
Branch (417:12): [True: 2, False: 3]
Branch (417:12): [True: 0, False: 0]
Branch (417:12): [True: 0, False: 1]
Branch (417:12): [True: 0, False: 0]
Branch (417:12): [True: 0, False: 1]
Branch (417:12): [True: 0, False: 0]
Branch (417:12): [True: 0, False: 9]
Branch (417:12): [True: 0, False: 0]
Branch (417:12): [True: 0, False: 4]
Branch (417:12): [True: 0, False: 2]
Branch (417:12): [Folded - Ignored]
Branch (417:12): [True: 0, False: 41]
Branch (417:12): [True: 0, False: 57]
|
418 | 2 | let items_to_unref = { |
419 | 2 | let mut state = self.state.lock_arc(); |
420 | 2 | self.evict_items(&mut *state).await |
421 | | }; |
422 | | // Unref items outside of lock |
423 | 4 | for item2 in items_to_unref { |
424 | 2 | item.unref().await; |
425 | | } |
426 | 4.49k | } |
427 | | |
428 | | // Now get the item |
429 | 4.49k | let mut state = self.state.lock_arc(); |
430 | 4.49k | let entry4.48k = state.lru.get_mut(key.borrow())?10 ; |
431 | 4.48k | entry.seconds_since_anchor = self.anchor_time.elapsed().as_secs() as i32; |
432 | 4.48k | Some(entry.data.clone()) |
433 | 4.49k | } |
434 | | |
435 | | /// Returns the replaced item if any. |
436 | 5.93k | pub async fn insert(&self, key: K, data: T) -> Option<T> |
437 | 5.93k | where |
438 | 5.93k | K: 'static, |
439 | 5.93k | { |
440 | 5.93k | self.insert_with_time(key, data, self.anchor_time.elapsed().as_secs() as i32) |
441 | 5.93k | .await |
442 | 5.93k | } |
443 | | |
444 | | /// Returns the replaced item if any. |
445 | 5.93k | pub async fn insert_with_time(&self, key: K, data: T, seconds_since_anchor: i32) -> Option<T> { |
446 | 5.93k | let items_to_unref = { |
447 | 5.93k | let mut state = self.state.lock_arc(); |
448 | 5.93k | self.inner_insert_many(&mut state, [(key, data)], seconds_since_anchor) |
449 | 5.93k | .await |
450 | | }; |
451 | | |
452 | | // Unref items outside of lock |
453 | 5.93k | let mut results = Vec::new(); |
454 | 5.99k | for item54 in items_to_unref { |
455 | 54 | item.unref().await; |
456 | 54 | results.push(item); |
457 | | } |
458 | | |
459 | 5.93k | results.into_iter().next() |
460 | 5.93k | } |
461 | | |
462 | | /// Same as `insert()`, but optimized for multiple inserts. |
463 | | /// Returns the replaced items if any. |
464 | 7 | pub async fn insert_many<It>(&self, inserts: It) -> Vec<T> |
465 | 7 | where |
466 | 7 | It: IntoIterator<Item = (K, T)> + Send, |
467 | 7 | // Note: It's not enough to have the inserts themselves be Send. The |
468 | 7 | // returned iterator should be Send as well. |
469 | 7 | <It as IntoIterator>::IntoIter: Send, |
470 | 7 | K: 'static, |
471 | 7 | { |
472 | 7 | let mut inserts = inserts.into_iter().peekable(); |
473 | | // Shortcut for cases where there are no inserts, so we don't need to lock. |
474 | 7 | if inserts.peek().is_none() { Branch (474:12): [True: 0, False: 0]
Branch (474:12): [Folded - Ignored]
Branch (474:12): [True: 1, False: 1]
Branch (474:12): [True: 4, False: 1]
Branch (474:12): [Folded - Ignored]
|
475 | 5 | return Vec::new(); |
476 | 2 | } |
477 | | |
478 | 2 | let items_to_unref = { |
479 | 2 | let state = &mut self.state.lock_arc(); |
480 | 2 | self.inner_insert_many(state, inserts, self.anchor_time.elapsed().as_secs() as i32) |
481 | 2 | .await |
482 | | }; |
483 | | |
484 | | // Unref items outside of lock |
485 | 2 | let mut results = Vec::new(); |
486 | 2 | for item0 in items_to_unref { |
487 | 0 | item.unref().await; |
488 | 0 | results.push(item); |
489 | | } |
490 | | |
491 | 2 | results |
492 | 7 | } |
493 | | |
494 | 5.94k | async fn inner_insert_many<It>( |
495 | 5.94k | &self, |
496 | 5.94k | state: &mut State<K, Q, T>, |
497 | 5.94k | inserts: It, |
498 | 5.94k | seconds_since_anchor: i32, |
499 | 5.94k | ) -> Vec<T> |
500 | 5.94k | where |
501 | 5.94k | It: IntoIterator<Item = (K, T)> + Send, |
502 | 5.94k | // Note: It's not enough to have the inserts themselves be Send. The |
503 | 5.94k | // returned iterator should be Send as well. |
504 | 5.94k | <It as IntoIterator>::IntoIter: Send, |
505 | 5.94k | { |
506 | 5.94k | let mut replaced_items = Vec::new(); |
507 | 11.8k | for (key5.94k , data5.94k ) in inserts { |
508 | 5.94k | let new_item_size = data.len(); |
509 | 5.94k | let eviction_item = EvictionItem { |
510 | 5.94k | seconds_since_anchor, |
511 | 5.94k | data, |
512 | 5.94k | }; |
513 | | |
514 | 5.94k | if let Some(old_item40 ) = state.put(&key, eviction_item).await { Branch (514:20): [True: 0, False: 0]
Branch (514:20): [True: 18, False: 5.73k]
Branch (514:20): [True: 0, False: 0]
Branch (514:20): [True: 0, False: 0]
Branch (514:20): [True: 0, False: 0]
Branch (514:20): [Folded - Ignored]
Branch (514:20): [True: 1, False: 1]
Branch (514:20): [True: 0, False: 27]
Branch (514:20): [True: 0, False: 3]
Branch (514:20): [True: 0, False: 0]
Branch (514:20): [True: 0, False: 1]
Branch (514:20): [True: 0, False: 4]
Branch (514:20): [True: 0, False: 1]
Branch (514:20): [True: 0, False: 2]
Branch (514:20): [True: 0, False: 2]
Branch (514:20): [True: 1, False: 1]
Branch (514:20): [True: 1, False: 1]
Branch (514:20): [True: 0, False: 1]
Branch (514:20): [True: 3, False: 13]
Branch (514:20): [True: 0, False: 0]
Branch (514:20): [True: 0, False: 2]
Branch (514:20): [Folded - Ignored]
Branch (514:20): [True: 16, False: 81]
Branch (514:20): [True: 0, False: 27]
|
515 | 40 | replaced_items.push(old_item); |
516 | 5.90k | } |
517 | 5.94k | state.sum_store_size += new_item_size; |
518 | 5.94k | state.lifetime_inserted_bytes.add(new_item_size); |
519 | | } |
520 | | |
521 | | // Perform eviction after all insertions |
522 | 5.94k | let items_to_unref = self.evict_items(state).await; |
523 | | |
524 | | // Note: We cannot drop the state lock here since we're borrowing it, |
525 | | // but the caller will handle unreffing these items after releasing the lock |
526 | 5.95k | for item14 in items_to_unref { |
527 | 14 | replaced_items.push(item); |
528 | 14 | } |
529 | | |
530 | 5.94k | replaced_items |
531 | 5.94k | } |
532 | | |
533 | 15 | pub async fn remove(&self, key: &Q) -> bool { |
534 | 15 | let (items_to_unref, removed_item) = { |
535 | 15 | let mut state = self.state.lock_arc(); |
536 | | |
537 | | // First perform eviction |
538 | 15 | let evicted_items = self.evict_items(&mut *state).await; |
539 | | |
540 | | // Then try to remove the requested item |
541 | 15 | let removed = if let Some(entry14 ) = state.lru.pop(key.borrow()) { Branch (541:34): [True: 0, False: 0]
Branch (541:34): [True: 0, False: 0]
Branch (541:34): [Folded - Ignored]
Branch (541:34): [True: 6, False: 0]
Branch (541:34): [True: 1, False: 0]
Branch (541:34): [True: 1, False: 1]
Branch (541:34): [True: 1, False: 0]
Branch (541:34): [True: 1, False: 0]
Branch (541:34): [True: 2, False: 0]
Branch (541:34): [True: 0, False: 0]
Branch (541:34): [True: 0, False: 0]
Branch (541:34): [True: 0, False: 0]
Branch (541:34): [True: 0, False: 0]
Branch (541:34): [True: 0, False: 0]
Branch (541:34): [True: 2, False: 0]
Branch (541:34): [True: 0, False: 0]
Branch (541:34): [True: 0, False: 0]
Branch (541:34): [Folded - Ignored]
Branch (541:34): [True: 0, False: 0]
|
542 | 14 | Some(state.remove(key, &entry, false).await) |
543 | | } else { |
544 | 1 | None |
545 | | }; |
546 | | |
547 | 15 | (evicted_items, removed) |
548 | | }; |
549 | | |
550 | | // Unref evicted items outside of lock |
551 | 16 | for item1 in items_to_unref { |
552 | 1 | item.unref().await; |
553 | | } |
554 | | |
555 | | // Unref removed item if any |
556 | 15 | if let Some(item14 ) = removed_item { Branch (556:16): [True: 0, False: 0]
Branch (556:16): [True: 0, False: 0]
Branch (556:16): [Folded - Ignored]
Branch (556:16): [True: 6, False: 0]
Branch (556:16): [True: 1, False: 0]
Branch (556:16): [True: 1, False: 1]
Branch (556:16): [True: 1, False: 0]
Branch (556:16): [True: 1, False: 0]
Branch (556:16): [True: 2, False: 0]
Branch (556:16): [True: 0, False: 0]
Branch (556:16): [True: 0, False: 0]
Branch (556:16): [True: 0, False: 0]
Branch (556:16): [True: 0, False: 0]
Branch (556:16): [True: 0, False: 0]
Branch (556:16): [True: 2, False: 0]
Branch (556:16): [True: 0, False: 0]
Branch (556:16): [True: 0, False: 0]
Branch (556:16): [Folded - Ignored]
Branch (556:16): [True: 0, False: 0]
|
557 | 14 | item.unref().await; |
558 | 14 | return true; |
559 | 1 | } |
560 | | |
561 | 1 | false |
562 | 15 | } |
563 | | |
564 | | /// Same as `remove()`, but allows for a conditional to be applied to the |
565 | | /// entry before removal in an atomic fashion. |
566 | 1 | pub async fn remove_if<F>(&self, key: &Q, cond: F) -> bool |
567 | 1 | where |
568 | 1 | F: FnOnce(&T) -> bool + Send, |
569 | 1 | { |
570 | 1 | let mut state = self.state.lock_arc(); |
571 | 1 | if let Some(entry) = state.lru.get(key.borrow()) { Branch (571:16): [True: 0, False: 0]
Branch (571:16): [Folded - Ignored]
Branch (571:16): [True: 0, False: 0]
Branch (571:16): [True: 0, False: 0]
Branch (571:16): [True: 0, False: 0]
Branch (571:16): [True: 0, False: 0]
Branch (571:16): [True: 1, False: 0]
Branch (571:16): [True: 0, False: 0]
Branch (571:16): [True: 0, False: 0]
Branch (571:16): [True: 0, False: 0]
Branch (571:16): [Folded - Ignored]
Branch (571:16): [True: 0, False: 0]
|
572 | 1 | if !cond(&entry.data) { Branch (572:16): [True: 0, False: 0]
Branch (572:16): [Folded - Ignored]
Branch (572:16): [True: 0, False: 0]
Branch (572:16): [True: 0, False: 0]
Branch (572:16): [True: 0, False: 0]
Branch (572:16): [True: 0, False: 0]
Branch (572:16): [True: 0, False: 1]
Branch (572:16): [True: 0, False: 0]
Branch (572:16): [True: 0, False: 0]
Branch (572:16): [True: 0, False: 0]
Branch (572:16): [Folded - Ignored]
Branch (572:16): [True: 0, False: 0]
|
573 | 0 | return false; |
574 | 1 | } |
575 | | // First perform eviction |
576 | 1 | let evicted_items = self.evict_items(&mut state).await; |
577 | | |
578 | | // Then try to remove the requested item |
579 | 1 | let removed_item = if let Some(entry) = state.lru.pop(key.borrow()) { Branch (579:39): [True: 0, False: 0]
Branch (579:39): [Folded - Ignored]
Branch (579:39): [True: 0, False: 0]
Branch (579:39): [True: 0, False: 0]
Branch (579:39): [True: 0, False: 0]
Branch (579:39): [True: 0, False: 0]
Branch (579:39): [True: 1, False: 0]
Branch (579:39): [True: 0, False: 0]
Branch (579:39): [True: 0, False: 0]
Branch (579:39): [True: 0, False: 0]
Branch (579:39): [Folded - Ignored]
Branch (579:39): [True: 0, False: 0]
|
580 | 1 | Some(state.remove(key, &entry, false).await) |
581 | | } else { |
582 | 0 | None |
583 | | }; |
584 | | |
585 | | // Drop the lock before unref operations |
586 | 1 | drop(state); |
587 | | |
588 | | // Unref evicted items |
589 | 1 | for item0 in evicted_items { |
590 | 0 | item.unref().await; |
591 | | } |
592 | | |
593 | | // Unref removed item if any |
594 | 1 | if let Some(item) = removed_item { Branch (594:20): [True: 0, False: 0]
Branch (594:20): [Folded - Ignored]
Branch (594:20): [True: 0, False: 0]
Branch (594:20): [True: 0, False: 0]
Branch (594:20): [True: 0, False: 0]
Branch (594:20): [True: 0, False: 0]
Branch (594:20): [True: 1, False: 0]
Branch (594:20): [True: 0, False: 0]
Branch (594:20): [True: 0, False: 0]
Branch (594:20): [True: 0, False: 0]
Branch (594:20): [Folded - Ignored]
Branch (594:20): [True: 0, False: 0]
|
595 | 1 | item.unref().await; |
596 | 1 | return true; |
597 | 0 | } |
598 | | |
599 | 0 | return false; |
600 | 0 | } |
601 | 0 | false |
602 | 1 | } |
603 | | |
604 | 5 | pub fn add_remove_callback(&self, callback: Box<dyn RemoveStateCallback<Q>>) { |
605 | 5 | self.state.lock_arc().add_remove_callback(callback); |
606 | 5 | } |
607 | | } |