Skip to main content

common_cache/
sync_cache.rs

1// Copyright 2023 Greptime Team
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
15use std::borrow::Borrow;
16use std::hash::Hash;
17use std::sync::Arc;
18
19use crate::RemovalCause;
20
21/// A weighted completed-value cache for synchronous callers.
22///
23/// Uses the same backend and removal causes as [`crate::Cache`]. Callers can
24/// serialize publication and invalidation with their own lock without awaiting
25/// under that lock. Values should be inexpensive to clone, such as `Arc<T>`.
26#[derive(Clone)]
27pub struct SyncCache<K, V> {
28    inner: moka::sync::Cache<K, V>,
29}
30
31impl<K, V> SyncCache<K, V>
32where
33    K: Eq + Hash + Send + Sync + 'static,
34    V: Clone + Send + Sync + 'static,
35{
36    /// Creates a cache with the caller's weight and eviction accounting.
37    pub fn new<W, L>(name: &str, capacity: u64, weigher: W, listener: L) -> Self
38    where
39        W: Fn(&K, &V) -> u32 + Send + Sync + 'static,
40        L: Fn(Arc<K>, V, RemovalCause) + Send + Sync + 'static,
41    {
42        Self {
43            inner: moka::sync::Cache::builder()
44                .name(name)
45                .max_capacity(capacity)
46                .weigher(weigher)
47                .eviction_listener(move |key, value, cause| listener(key, value, cause.into()))
48                .build(),
49        }
50    }
51
52    /// Gets a completed value without starting a load.
53    pub fn get<Q>(&self, key: &Q) -> Option<V>
54    where
55        K: Borrow<Q>,
56        Q: Hash + Eq + ?Sized,
57    {
58        self.inner.get(key)
59    }
60
61    /// Submits a completed value to the cache's admission and eviction policy.
62    /// Insertion does not guarantee retention.
63    pub fn insert(&self, key: K, value: V) {
64        self.inner.insert(key, value);
65    }
66
67    /// Applies pending admission, eviction, and accounting work.
68    pub fn run_pending_tasks(&self) {
69        self.inner.run_pending_tasks();
70    }
71
72    /// Invalidates a completed entry. Concurrent producers are fenced by the caller.
73    pub fn invalidate<Q>(&self, key: &Q)
74    where
75        K: Borrow<Q>,
76        Q: Hash + Eq + ?Sized,
77    {
78        self.inner.invalidate(key);
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use std::sync::Mutex;
85
86    use super::*;
87
88    #[test]
89    fn test_borrowed_lookup_and_invalidation() {
90        let removed = Arc::new(Mutex::new(Vec::new()));
91        let events = removed.clone();
92        let cache = SyncCache::new(
93            "test",
94            1024,
95            |_: &String, _: &Arc<usize>| 1,
96            move |key, value, cause| events.lock().unwrap().push((key, value, cause)),
97        );
98        let value = Arc::new(7);
99        cache.insert("a".to_string(), value.clone());
100        cache.insert("b".to_string(), Arc::new(9));
101        assert!(Arc::ptr_eq(&cache.get("a").unwrap(), &value));
102        cache.invalidate("a");
103        assert!(cache.get("a").is_none());
104        assert_eq!(*cache.get("b").unwrap(), 9);
105        cache.run_pending_tasks();
106        assert_eq!(
107            *removed.lock().unwrap(),
108            vec![(Arc::new("a".to_string()), value, RemovalCause::Explicit)]
109        );
110    }
111}