Skip to main content

fxfs/lsm_tree/
cache.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use super::types::{Key, Value};
6use fuchsia_sync::MappedMutexGuard;
7use std::fmt;
8
9pub trait ObjectCachePlaceholder<V: Value>: Send + Sync {
10    /// Consumes itself in delivering the cache value for which the placeholder was reserved.
11    /// Passing None for value should not be inserted into the cache, but interpreted as an
12    /// incomplete search.
13    fn complete(self: Box<Self>, value: Option<&V>);
14}
15
16/// Possible results for a cache `lookup_or_reserve()`
17pub enum ObjectCacheResult<'a, V: Value> {
18    /// Contains the value successfully retrieved from the cache.
19    Value(MappedMutexGuard<'a, V>),
20    /// The object was not found in the cache, so this placeholder can be used to insert the
21    /// calculated result.
22    Placeholder(Box<dyn ObjectCachePlaceholder<V> + 'a>),
23    /// Returned for items that are not wanted to be inserted into the cache.
24    NoCache,
25}
26
27impl<'a, V: Value> fmt::Debug for ObjectCacheResult<'a, V> {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        let (name, contents) = match self {
30            Self::Value(v) => ("Value", Some(format!("{:?}", v))),
31            Self::NoCache => ("NoCache", None),
32            Self::Placeholder(_) => ("Placeholder", None),
33        };
34        if contents.is_some() {
35            f.debug_struct("ObjectCacheResult").field(name, &contents.unwrap()).finish()
36        } else {
37            f.debug_struct("ObjectCacheResult").field(name, &"").finish()
38        }
39    }
40}
41
42pub trait ObjectCache<K: Key, V: Value>: Send + Sync {
43    /// Looks up a key in the cache and may return a cached value for it. See `ObjectCacheResult`.
44    fn lookup_or_reserve<'a>(&'a self, key: &K) -> ObjectCacheResult<'a, V>;
45
46    /// Returns whether the key is cacheable. This is used by clients to avoid cloning keys and
47    /// values when calling `invalidate` would do nothing.
48    ///
49    /// Must be consistent with `lookup_or_reserve` returning `NoCache`.
50    fn is_cacheable(&self, key: &K) -> bool;
51
52    /// Removes key from cache if `value` is None, invalidates the results of placeholders that have
53    /// not been resolved. When `value` is provided then the value may be inserted, and may replace
54    /// an existing value.
55    fn invalidate(&self, key: &K, value: Option<V>);
56
57    /// Clears all entries from the cache.
58    fn clear(&self);
59
60    /// Returns the number of cached entries (for testing/diagnostics).
61    fn len(&self) -> usize {
62        0
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69    use crate::object_store::object_record::{ObjectKey, ObjectValue};
70    use fuchsia_sync::{Mutex, MutexGuard};
71
72    struct DummyPlaceholder;
73    impl ObjectCachePlaceholder<ObjectValue> for DummyPlaceholder {
74        fn complete(self: Box<Self>, _value: Option<&ObjectValue>) {}
75    }
76
77    struct DummyCache;
78    impl ObjectCache<ObjectKey, ObjectValue> for DummyCache {
79        fn lookup_or_reserve(&self, _key: &ObjectKey) -> ObjectCacheResult<'_, ObjectValue> {
80            ObjectCacheResult::NoCache
81        }
82        fn is_cacheable(&self, _key: &ObjectKey) -> bool {
83            false
84        }
85        fn invalidate(&self, _key: &ObjectKey, _value: Option<ObjectValue>) {}
86        fn clear(&self) {}
87    }
88
89    #[fuchsia::test]
90    fn test_object_cache_result_debug_and_default_len() {
91        let mutex = Mutex::new(ObjectValue::None);
92        let guard = MutexGuard::map(mutex.lock(), |v| v);
93        let val_res: ObjectCacheResult<'_, ObjectValue> = ObjectCacheResult::Value(guard);
94        assert!(format!("{:?}", val_res).contains("Value"));
95
96        let placeholder_res: ObjectCacheResult<'_, ObjectValue> =
97            ObjectCacheResult::Placeholder(Box::new(DummyPlaceholder));
98        assert!(format!("{:?}", placeholder_res).contains("Placeholder"));
99
100        let no_cache_res: ObjectCacheResult<'_, ObjectValue> = ObjectCacheResult::NoCache;
101        assert!(format!("{:?}", no_cache_res).contains("NoCache"));
102
103        let cache = DummyCache;
104        assert_eq!(cache.len(), 0);
105        assert!(!cache.is_cacheable(&ObjectKey::object(1)));
106        assert!(matches!(
107            cache.lookup_or_reserve(&ObjectKey::object(1)),
108            ObjectCacheResult::NoCache
109        ));
110        cache.invalidate(&ObjectKey::object(1), None);
111        cache.clear();
112        Box::new(DummyPlaceholder).complete(None);
113    }
114}