Coverage Report

Created: 2025-07-30 16:11

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