Skip to main content

zino_extra/cache/
mod.rs

1//! Global cache for the application.
2
3use lru::LruCache;
4use parking_lot::RwLock;
5use std::num::NonZeroUsize;
6use zino_core::{JsonValue, LazyLock, state::State};
7
8/// Global cache built on the top of [`LruCache`].
9#[derive(Debug, Clone, Copy, Default)]
10pub struct GlobalCache;
11
12impl GlobalCache {
13    /// Puts a key-value pair into the global cache.
14    /// If the key already exists in the cache, then it updates the key’s value and
15    /// returns the old value. Otherwise, `None` is returned.
16    #[inline]
17    pub fn put(key: impl Into<String>, value: impl Into<JsonValue>) -> Option<JsonValue> {
18        let mut cache = GLOBAL_CACHE.write();
19        cache.put(key.into(), value.into())
20    }
21
22    /// Pushes a key-value pair into the global cache. If an entry with the key already
23    /// exists in the cache or another cache entry is removed (due to the LRU’s capacity),
24    /// then it returns the old entry’s key-value pair. Otherwise, returns `None`.
25    #[inline]
26    pub fn push(
27        key: impl Into<String>,
28        value: impl Into<JsonValue>,
29    ) -> Option<(String, JsonValue)> {
30        let mut cache = GLOBAL_CACHE.write();
31        cache.push(key.into(), value.into())
32    }
33
34    /// Returns a cloned value of the key in the global cache or `None`
35    /// if it is not present in the cache. Moves the key to the head of the LRU list if it exists.
36    #[inline]
37    pub fn get(key: &str) -> Option<JsonValue> {
38        let mut cache = GLOBAL_CACHE.write();
39        cache.get(key).cloned()
40    }
41
42    /// Returns a cloned value of the key in the global cache or `None`
43    /// if it is not present in the cache. It does not update the LRU list
44    /// so the key’s position will be unchanged.
45    #[inline]
46    pub fn peek(key: &str) -> Option<JsonValue> {
47        let cache = GLOBAL_CACHE.read();
48        cache.peek(key).cloned()
49    }
50
51    /// Returns a bool indicating whether the given key is in the global cache.
52    /// Does not update the LRU list.
53    #[inline]
54    pub fn contains(key: &str) -> bool {
55        let cache = GLOBAL_CACHE.read();
56        cache.contains(key)
57    }
58
59    /// Removes and returns the value corresponding to the key from the global cache or
60    /// `None` if it does not exist.
61    #[inline]
62    pub fn pop(key: &str) -> Option<JsonValue> {
63        let mut cache = GLOBAL_CACHE.write();
64        cache.pop(key)
65    }
66
67    /// Removes and returns the key-value pair from the global cache or
68    /// `None` if it does not exist.
69    #[inline]
70    pub fn pop_entry(key: &str) -> Option<(String, JsonValue)> {
71        let mut cache = GLOBAL_CACHE.write();
72        cache.pop_entry(key)
73    }
74
75    /// Removes and returns the key-value pair corresponding to the least recently used item
76    /// or `None` if the global cache is empty.
77    #[inline]
78    pub fn pop_lru() -> Option<(String, JsonValue)> {
79        let mut cache = GLOBAL_CACHE.write();
80        cache.pop_lru()
81    }
82
83    /// Marks the key as the most recently used one.
84    #[inline]
85    pub fn promote(key: &str) -> bool {
86        let mut cache = GLOBAL_CACHE.write();
87        cache.promote(key)
88    }
89
90    /// Marks the key as the least recently used one.
91    #[inline]
92    pub fn demote(key: &str) -> bool {
93        let mut cache = GLOBAL_CACHE.write();
94        cache.demote(key)
95    }
96
97    /// Returns the number of key-value pairs that are currently in the global cache.
98    #[inline]
99    pub fn len() -> usize {
100        let cache = GLOBAL_CACHE.read();
101        cache.len()
102    }
103
104    /// Returns a bool indicating whether the global cache is empty or not.
105    #[inline]
106    pub fn is_empty() -> bool {
107        let cache = GLOBAL_CACHE.read();
108        cache.is_empty()
109    }
110
111    /// Returns the maximum number of key-value pairs the global cache can hold.
112    #[inline]
113    pub fn cap() -> NonZeroUsize {
114        let cache = GLOBAL_CACHE.read();
115        cache.cap()
116    }
117
118    /// Resizes the global cache. If the new capacity is smaller than the size of
119    /// the current cache any entries past the new capacity are discarded.
120    #[inline]
121    pub fn resize(cap: NonZeroUsize) {
122        let mut cache = GLOBAL_CACHE.write();
123        cache.resize(cap)
124    }
125
126    /// Clears the contents of the global cache.
127    pub fn clear() {
128        let mut cache = GLOBAL_CACHE.write();
129        cache.clear()
130    }
131}
132
133/// Global cache.
134static GLOBAL_CACHE: LazyLock<RwLock<LruCache<String, JsonValue>>> = LazyLock::new(|| {
135    let capacity = if let Some(cache) = State::shared().get_config("cache") {
136        cache
137            .get("capacity")
138            .expect("field `capacity` is missing")
139            .as_integer()
140            .expect("field `capacity` should be an integer")
141            .try_into()
142            .expect("field `capacity` should be a positive integer")
143    } else {
144        10000
145    };
146    RwLock::new(LruCache::new(
147        NonZeroUsize::new(capacity).unwrap_or(NonZeroUsize::MIN),
148    ))
149});