1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
// Copyright 2019 Conflux Foundation. All rights reserved.
// Conflux is free software and distributed under GNU General Public License.
// See http://www.gnu.org/licenses/
#[derive(Clone, MallocSizeOfDerive)]
pub enum TrieCacheSlotOrCacheAlgoData<CacheAlgoDataT: CacheAlgoDataTrait> {
TrieCacheSlot(ActualSlabIndex),
CacheAlgoData(CacheAlgoDataT),
}
// TODO(yz): Rename class and explain how this class interact with the lifecycle
// of trie node.
/// CacheableNodeRef maintains the information of cached node and possibly
/// non-cached children of cached node.
///
/// Only CacheableNodeRef for Delta MPT is currently implemented.
///
/// CacheableNodeRef for persistent MPT may add a field for storage access key,
/// and a reference count. For persistent MPT, NodeRef (storage access key) for
/// non-cached node might be kept, to be able to read child node directly from
/// disk, otherwise the program have to read the current node again to first
/// obtain the CacheableNodeRef for the non-cached child node. However a
/// reference count is necessary to prevent NodeRef for non-cached node from
/// staying forever in the memory.
#[derive(Clone, MallocSizeOfDerive)]
pub struct CacheableNodeRefDeltaMpt<CacheAlgoDataT: CacheAlgoDataTrait> {
cached: TrieCacheSlotOrCacheAlgoData<CacheAlgoDataT>,
}
impl<CacheAlgoDataT: CacheAlgoDataTrait>
CacheableNodeRefDeltaMpt<CacheAlgoDataT>
{
pub fn new(cached: TrieCacheSlotOrCacheAlgoData<CacheAlgoDataT>) -> Self {
Self { cached }
}
pub fn get_cache_info(
&self,
) -> &TrieCacheSlotOrCacheAlgoData<CacheAlgoDataT> {
&self.cached
}
pub fn get_slot(&self) -> Option<&ActualSlabIndex> {
match &self.cached {
TrieCacheSlotOrCacheAlgoData::CacheAlgoData(_) => None,
TrieCacheSlotOrCacheAlgoData::TrieCacheSlot(slot) => Some(slot),
}
}
}
/// Generally, the db key of MPT node is the merkle hash, however it consumes
/// too much memory. For Delta MPT, the total number of nodes at 1000tps are
/// relative small compared to memory consumed by Cached TrieNodes, and we don't
/// need to persist, therefore we could use "row number" as db key.
pub type DeltaMptDbKey = RowNumberUnderlyingType;
// Each DeltaMpt is assigned an index. There are not many DeltaMpts to keep at a
// time.
pub type DeltaMptId = u16;
const MPT_ID_RANGE: usize = std::u16::MAX as usize + 1;
// TODO(yz): Optimize the access like NodeRefMapDeltaMpt. Keep a number of
// TODO(yz): chunks, each delta mpt maintains a deque of chunks. Whenever all
// TODO(yz): chunks are filled, reclaim one chunk from the mpt with the lowest
// TODO(yz): chunk cached rate.
/// Maintains the cache slot / cache info for multiple Delta MPTs.
#[derive(MallocSizeOfDerive)]
pub struct NodeRefMapDeltaMpts<CacheAlgoDataT: CacheAlgoDataTrait> {
node_ref_maps:
Vec<HashMap<DeltaMptDbKey, CacheableNodeRefDeltaMpt<CacheAlgoDataT>>>,
nodes: usize,
}
impl<CacheAlgoDataT: CacheAlgoDataTrait> NodeRefMapDeltaMpts<CacheAlgoDataT> {
pub fn new() -> Self {
let mut node_ref_maps = Vec::with_capacity(MPT_ID_RANGE);
for _i in 0..MPT_ID_RANGE {
node_ref_maps.push(Default::default());
}
Self {
node_ref_maps,
nodes: 0,
}
}
/// Insert may crash due to memory allocation issue, although it won't
/// return error in current implementation.
pub fn insert(
&mut self, key: (DeltaMptId, DeltaMptDbKey), slot: ActualSlabIndex,
) -> Result<()> {
if self.node_ref_maps[key.0 as usize]
.insert(
key.1,
CacheableNodeRefDeltaMpt {
cached: TrieCacheSlotOrCacheAlgoData::TrieCacheSlot(slot),
},
)
.is_none()
{
self.nodes += 1;
};
Ok(())
}
/// The cache_info is only valid when the element still lives in cache.
/// Therefore we return the reference to the cache_info to represent the
/// lifetime requirement.
pub fn get_cache_info(
&self, key: (DeltaMptId, DeltaMptDbKey),
) -> Option<&CacheableNodeRefDeltaMpt<CacheAlgoDataT>> {
self.node_ref_maps[key.0 as usize].get(&key.1)
}
pub fn set_cache_info(
&mut self, key: (DeltaMptId, DeltaMptDbKey),
cache_info: CacheableNodeRefDeltaMpt<CacheAlgoDataT>,
) {
if self.node_ref_maps[key.0 as usize]
.insert(key.1, cache_info)
.is_none()
{
self.nodes += 1;
}
}
pub fn delete(
&mut self, key: (DeltaMptId, DeltaMptDbKey),
) -> Option<CacheableNodeRefDeltaMpt<CacheAlgoDataT>> {
let maybe_old_value = self.node_ref_maps[key.0 as usize].remove(&key.1);
if maybe_old_value.is_some() {
self.nodes -= 1;
}
maybe_old_value
}
pub fn get_all_cache_infos_from_mpt(
&self, mpt_id: DeltaMptId,
) -> Vec<(DeltaMptDbKey, CacheableNodeRefDeltaMpt<CacheAlgoDataT>)> {
self.node_ref_maps[mpt_id as usize]
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect()
}
pub fn log_usage(&self) {
debug!("node_ref_map.BTreeMap: #nodes: {}", self.nodes,);
}
}
pub const DEFAULT_NODE_MAP_SIZE: u32 = 200_000_000;
use super::{
super::errors::*, cache::algorithm::CacheAlgoDataTrait,
node_memory_manager::ActualSlabIndex, row_number::RowNumberUnderlyingType,
};
use hashbrown::HashMap;
use malloc_size_of_derive::MallocSizeOf as MallocSizeOfDerive;