Skip to content

Commit bbdf3e8

Browse files
committed
[Layered Map] Expose inner layers
Previously, in order to find all changes between two `State`s, we could just "diff" the two map layers and we would get all the KVs that are changed. This is insufficient with tiered storage. For instance, consider the following sequence of events: 1. At the end of block `N`, key `K` is read and promoted to hot and its value is `V0` in both hot state and cold state. 2. At the end of block `N+1`, its value is updated to `V1` in hot state. 3. At the end of block `N+2`, it's evicted and the value in cold state is updated to `V1`. 4. At the end of block `N+3`, it's read and promoted into hot state again. If we simply look at the `StateDelta` in the current implementation, we'd see the value change in hot state, but miss the change from `V0` to `V1` in the cold state. So we need to expose all the inner layers in order to accurately compute what changed between two points in time. This PR simply adds the API on `LayeredMap`.
1 parent 7eea285 commit bbdf3e8

File tree

3 files changed

+50
-3
lines changed

3 files changed

+50
-3
lines changed

experimental/storage/layered-map/src/layer.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,16 @@ use aptos_crypto::HashValue;
1111
use aptos_drop_helper::ArcAsyncDrop;
1212
use aptos_infallible::Mutex;
1313
use aptos_metrics_core::IntGaugeVecHelper;
14-
use std::{marker::PhantomData, sync::Arc};
14+
use std::{
15+
marker::PhantomData,
16+
sync::{Arc, Weak},
17+
};
1518

1619
#[derive(Debug)]
1720
struct LayerInner<K: ArcAsyncDrop, V: ArcAsyncDrop> {
1821
peak: FlattenPerfectTree<K, V>,
19-
children: Mutex<Vec<Arc<LayerInner<K, V>>>>,
22+
parent: Weak<Self>,
23+
children: Mutex<Vec<Arc<Self>>>,
2024
use_case: &'static str,
2125
family: HashValue,
2226
layer: u64,
@@ -50,6 +54,7 @@ impl<K: ArcAsyncDrop, V: ArcAsyncDrop> LayerInner<K, V> {
5054
let family = HashValue::random();
5155
Arc::new(Self {
5256
peak: FlattenPerfectTree::new_with_empty_nodes(1),
57+
parent: Weak::new(),
5358
children: Mutex::new(Vec::new()),
5459
use_case,
5560
family,
@@ -61,6 +66,7 @@ impl<K: ArcAsyncDrop, V: ArcAsyncDrop> LayerInner<K, V> {
6166
fn spawn(self: &Arc<Self>, child_peak: FlattenPerfectTree<K, V>, base_layer: u64) -> Arc<Self> {
6267
let child = Arc::new(Self {
6368
peak: child_peak,
69+
parent: Arc::downgrade(self),
6470
children: Mutex::new(Vec::new()),
6571
use_case: self.use_case,
6672
family: self.family,
@@ -108,6 +114,10 @@ impl<K: ArcAsyncDrop, V: ArcAsyncDrop> MapLayer<K, V> {
108114
}
109115
}
110116

117+
pub(crate) fn parent(&self) -> Option<Self> {
118+
self.inner.parent.upgrade().map(Self::new)
119+
}
120+
111121
pub fn into_layers_view_after(self, base_layer: MapLayer<K, V>) -> LayeredMap<K, V> {
112122
assert!(base_layer.is_family(&self));
113123
assert!(base_layer.inner.layer >= self.inner.base_layer);

experimental/storage/layered-map/src/map/mod.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,27 @@ where
114114
.into_feet_iter()
115115
.flat_map(|node| DescendantIterator::new(node, self.base_layer()))
116116
}
117+
118+
/// For example, if `self` is `(2, 5]`, this returns `(2, 3], (3, 4], (4, 5]` as a list.
119+
pub fn inner_maps(&self) -> Vec<Self> {
120+
let num_layers = (self.top_layer.layer() - self.base_layer.layer()) as usize;
121+
let mut ret = Vec::with_capacity(num_layers);
122+
123+
let mut current = self.top_layer.clone();
124+
while current.layer() > self.base_layer.layer() {
125+
let parent = current.parent().expect("The lower layer must exist.");
126+
ret.push(Self::new(parent.clone(), current));
127+
current = parent;
128+
}
129+
ret.reverse();
130+
131+
assert_eq!(
132+
ret.len(),
133+
num_layers,
134+
"The number of inner maps ({}) does not match num_layers ({})",
135+
ret.len(),
136+
num_layers
137+
);
138+
ret
139+
}
117140
}

experimental/storage/layered-map/src/tests.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ impl Hash for HashCollide {
1919
}
2020
}
2121

22+
fn naive_view_layer<K: Ord, V>(layer: Vec<(K, V)>) -> BTreeMap<K, V> {
23+
layer.into_iter().collect()
24+
}
25+
2226
fn naive_view_layers<K: Ord, V>(layers: impl Iterator<Item = Vec<(K, V)>>) -> BTreeMap<K, V> {
2327
layers.flat_map(|layer| layer.into_iter()).collect()
2428
}
@@ -89,7 +93,8 @@ proptest! {
8993
let layered_map = top_layer.into_layers_view_after(base_layer);
9094

9195
// n.b. notice items_per_update doesn't have a placeholder for the root layer
92-
let all = naive_view_layers(items_per_update.drain(base..top));
96+
let items_per_update = items_per_update.drain(base..top).collect_vec();
97+
let all = naive_view_layers(items_per_update.clone().into_iter());
9398

9499
// get() individually
95100
for (k, v) in &all {
@@ -99,6 +104,15 @@ proptest! {
99104
// traversed via iterator
100105
let traversed = layered_map.iter().collect();
101106
prop_assert_eq!(all, traversed);
107+
108+
for (inner_map, items) in layered_map.inner_maps().into_iter().zip_eq(items_per_update.into_iter()) {
109+
let all = naive_view_layer(items);
110+
for (k, v) in &all {
111+
prop_assert_eq!(inner_map.get(k), Some(*v));
112+
}
113+
let traversed = inner_map.iter().collect();
114+
prop_assert_eq!(all, traversed);
115+
}
102116
}
103117

104118
#[test]

0 commit comments

Comments
 (0)