cfx_storage/impls/delta_mpt/cache_manager_delta_mpts.rs
1// Copyright 2019 Conflux Foundation. All rights reserved.
2// Conflux is free software and distributed under GNU General Public License.
3// See http://www.gnu.org/licenses/
4
5// TODO: On performance, each access may requires a lock because of calling
6// TODO: cache algorithm & cache eviction & TrieNode slab alloc/delete
7// TODO: & noderefmap update. The read & write can not be easily broken
8// TODO: down because of dependency. Calling cache algorithm always
9// TODO: requires write lock to algorithm. cache hit updates can be
10// TODO: batched locally. The caller knows whether a node is hit. But the
11// TODO: element for batched hit update could be evicted by other threads. cache
12// TODO: miss locks mutably for slab alloc/delete, noderefmap update.
13// TODO: can also be batched however the lifetime of TrieNode should be managed.
14#[derive(MallocSizeOfDerive)]
15pub struct CacheManagerDeltaMpts<
16 CacheAlgoDataT: CacheAlgoDataTrait,
17 CacheAlgorithmT: CacheAlgorithm<CacheAlgoData = CacheAlgoDataT>,
18> {
19 /// One of the key problem in implementing a cache for tree node is that,
20 /// when a node is swapped-out from cache into disk, the eviction of
21 /// children should be independent, not only because of cache hit
22 /// property, but also because that a node can have multiple parents. When
23 /// a node is loaded into cache again, it should automatically connects
24 /// to its children in cache, even if the children is shared with some
25 /// other node unknown.
26 ///
27 /// Another problem is that, when a node is swapped-out, the parent's
28 /// children reference must be updated unless the children reference is
29 /// the db key, or something stable. The db key in Conflux is the
30 /// Merkle Hash, which is too large for a Trie node: 16*64B are
31 /// required to store only ChildrenTable.
32 ///
33 /// To solve these problems, we introduce CacheableNodeRef, which should
34 /// remain stable for the lifetime of the TrieNode of the
35 /// ChildrenTable. The key of NodeRefMap shall be db key, and the value
36 /// of NodeRefMap shall point to where the node is cached.
37 ///
38 /// The db key could be made smaller for Delta MPT (4B)
39 /// and maybe for Persistent MPT (8B) by simply using the row number.
40 ///
41 /// If we have to use Merkle Hash (64B) as db key for Persistent MPT,
42 /// storing the key for non-cached node is costly. There are two options,
43 /// a) store key only for cached nodes, there is little addition cost (8B),
44 /// however to load an un-cached child node from disk, caller should first
45 /// read the child's access key by loading the current node, then check
46 /// NodeRefMap, if actual missing load it from disk for the second
47 /// time.
48 /// b) create NodeRef for some children of cached node, store the 64B key
49 /// for disk access, and keep the reference count so that we don't store
50 /// NodeRef for nodes which later becomes irrelevant indefinitely.
51 ///
52 /// Note that there are also dirty nodes, which always live in memory.
53 /// The NodeRef / MaybeNodeRef also covers dirty nodes, but NodeRefMap
54 /// covers only committed nodes.
55 pub(super) node_ref_map: NodeRefMapDeltaMpts<CacheAlgoDataT>,
56 pub(super) cache_algorithm: CacheAlgorithmT,
57}
58
59impl CacheIndexTrait for (DeltaMptId, DeltaMptDbKey) {}
60
61impl<
62 CacheAlgoDataT: CacheAlgoDataTrait,
63 CacheAlgorithmT: CacheAlgorithm<
64 CacheAlgoData = CacheAlgoDataT,
65 CacheIndex = (DeltaMptId, DeltaMptDbKey),
66 >,
67 > CacheManagerDeltaMpts<CacheAlgoDataT, CacheAlgorithmT>
68{
69 pub fn insert_to_node_ref_map_and_call_cache_access(
70 &mut self, cache_index: (DeltaMptId, DeltaMptDbKey),
71 slot: ActualSlabIndex,
72 node_memory_manager: &NodeMemoryManager<
73 CacheAlgoDataT,
74 CacheAlgorithmT,
75 >,
76 ) -> Result<()> {
77 self.node_ref_map.insert(cache_index, slot)?;
78 node_memory_manager.call_cache_algorithm_access(self, cache_index);
79 Ok(())
80 }
81
82 pub fn is_cached(&self, cache_index: (DeltaMptId, DeltaMptDbKey)) -> bool {
83 if let Some(cache_info) = self.node_ref_map.get_cache_info(cache_index)
84 {
85 cache_info.get_slot().is_some()
86 } else {
87 false
88 }
89 }
90
91 pub fn log_usage(&self) {
92 self.node_ref_map.log_usage();
93 self.cache_algorithm.log_usage("trie node cache ");
94 }
95}
96
97use super::{
98 super::errors::*,
99 cache::algorithm::{CacheAlgoDataTrait, CacheAlgorithm, CacheIndexTrait},
100 node_memory_manager::{ActualSlabIndex, NodeMemoryManager},
101 node_ref_map::{DeltaMptDbKey, DeltaMptId, NodeRefMapDeltaMpts},
102};
103use malloc_size_of_derive::MallocSizeOf as MallocSizeOfDerive;