oasis_core_runtime/
cache.rs

1//! In-memory cache of trees.
2use std::{
3    cell::RefCell,
4    num::NonZeroUsize,
5    rc::Rc,
6    sync::{Arc, Mutex, MutexGuard},
7};
8
9use crate::{
10    common::crypto::hash::Hash,
11    protocol::Protocol,
12    storage::mkvs::{sync::HostReadSyncer, Root, Tree},
13    types::HostStorageEndpoint,
14};
15
16thread_local! {
17    static QUERY_CACHE: RefCell<lru::LruCache<u64, Rc<RefCell<Cache>>>> = RefCell::new(lru::LruCache::new(NonZeroUsize::new(10).unwrap()));
18}
19
20/// A set of storage tree caches, one for each storage operation:
21///
22/// * **Execution** and **checking** of transactions each have their own cache guarded by a mutex
23///   since the usual use case is that only one execution/check batch is running at any given time.
24///
25/// * **Queries** have a thread-local cache as there can be multiple queries running at any given
26///   time and having a global lock would kill concurrency.
27#[derive(Clone)]
28pub struct CacheSet {
29    protocol: Arc<Protocol>,
30    execute: Arc<Mutex<Cache>>,
31    check: Arc<Mutex<Cache>>,
32}
33
34impl CacheSet {
35    /// Create a new empty cache set.
36    pub fn new(protocol: Arc<Protocol>) -> Self {
37        Self {
38            execute: Arc::new(Mutex::new(Cache::new(&protocol))),
39            check: Arc::new(Mutex::new(Cache::new(&protocol))),
40            protocol,
41        }
42    }
43
44    /// Cache used for executing transactions.
45    pub fn execute(&self, root: Root) -> MutexGuard<'_, Cache> {
46        let mut cache = self.execute.lock().unwrap();
47        cache.maybe_replace(&self.protocol, root);
48        cache
49    }
50
51    /// Cache used for checking transactions.
52    pub fn check(&self, root: Root) -> MutexGuard<'_, Cache> {
53        let mut cache = self.check.lock().unwrap();
54        cache.maybe_replace(&self.protocol, root);
55        cache
56    }
57
58    /// Cache used for queries.
59    pub fn query(&self, root: Root) -> Rc<RefCell<Cache>> {
60        let cache = QUERY_CACHE.with(|caches| {
61            let mut caches = caches.borrow_mut();
62            if let Some(cache) = caches.get(&root.version) {
63                return cache.clone();
64            }
65
66            let cache = Rc::new(RefCell::new(Cache::new(&self.protocol)));
67            caches.put(root.version, cache.clone());
68            cache
69        });
70        cache.borrow_mut().maybe_replace(&self.protocol, root);
71        cache
72    }
73}
74
75/// Cached storage tree with an associated root.
76pub struct Cache {
77    root: Root,
78    tree: Tree,
79}
80
81impl Cache {
82    fn new(protocol: &Arc<Protocol>) -> Self {
83        Self {
84            root: Default::default(),
85            tree: Self::build(protocol, Default::default()),
86        }
87    }
88
89    fn build(protocol: &Arc<Protocol>, root: Root) -> Tree {
90        let config = protocol.get_config();
91        let read_syncer = HostReadSyncer::new(protocol.clone(), HostStorageEndpoint::Runtime);
92        Tree::builder()
93            .with_capacity(
94                config.storage.cache_node_capacity,
95                config.storage.cache_value_capacity,
96            )
97            .with_root(root)
98            .build(Box::new(read_syncer))
99    }
100
101    fn maybe_replace(&mut self, protocol: &Arc<Protocol>, root: Root) {
102        if self.root == root {
103            return;
104        }
105
106        self.tree = Self::build(protocol, root);
107        self.root = root;
108    }
109
110    /// Reference to the cached tree.
111    pub fn tree(&self) -> &Tree {
112        &self.tree
113    }
114
115    /// Mutable reference to the cached tree.
116    pub fn tree_mut(&mut self) -> &mut Tree {
117        &mut self.tree
118    }
119
120    /// Commits a specific version and root as being stored by the tree.
121    pub fn commit(&mut self, version: u64, hash: Hash) {
122        self.root.version = version;
123        self.root.hash = hash;
124    }
125}