Skip to content

Commit 827da80

Browse files
committed
starknet_patricia_storage: two layer storage
1 parent 1e02db5 commit 827da80

3 files changed

Lines changed: 129 additions & 0 deletions

File tree

crates/starknet_patricia_storage/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ pub mod short_key_storage;
1515
#[cfg(test)]
1616
pub mod storage_test;
1717
pub mod storage_trait;
18+
pub mod two_layer_storage;
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
use crate::storage_trait::{
2+
DbKey,
3+
DbValue,
4+
ImmutableReadOnlyStorage,
5+
PatriciaStorageResult,
6+
ReadOnlyStorage,
7+
};
8+
9+
#[cfg(test)]
10+
#[path = "two_layer_storage_test.rs"]
11+
mod two_layer_storage_test;
12+
13+
/// Overlay storage on top of a borrowed base [`ImmutableReadOnlyStorage`]: `overlay` is consulted
14+
/// first via [`ImmutableReadOnlyStorage::get`] / [`ImmutableReadOnlyStorage::mget`]; on miss,
15+
/// reads relay to `base`.
16+
///
17+
/// On misses, [`ReadOnlyStorage::get_mut`] / [`ReadOnlyStorage::mget_mut`] use get/mget on the
18+
/// underlying storage to allow the composite to implement [`ReadOnlyStorage`] while holding `&'a
19+
/// Base`. This allows passing `TwoLayerStorage` to `fetch_all_patricia_paths`, which requires
20+
/// `&mut` [`ReadOnlyStorage`].
21+
pub struct TwoLayerStorage<'a, Overlay, Base>
22+
where
23+
Overlay: ImmutableReadOnlyStorage + Sync,
24+
Base: ImmutableReadOnlyStorage + Sync + ?Sized,
25+
{
26+
overlay: Overlay,
27+
base: &'a Base,
28+
}
29+
30+
impl<'a, Overlay, Base> TwoLayerStorage<'a, Overlay, Base>
31+
where
32+
Overlay: ImmutableReadOnlyStorage + Sync,
33+
Base: ImmutableReadOnlyStorage + Sync + ?Sized,
34+
{
35+
pub fn new(overlay: Overlay, base: &'a Base) -> Self {
36+
Self { overlay, base }
37+
}
38+
}
39+
40+
impl<'a, Overlay, Base> ReadOnlyStorage for TwoLayerStorage<'a, Overlay, Base>
41+
where
42+
Overlay: ImmutableReadOnlyStorage + Sync,
43+
Base: ImmutableReadOnlyStorage + Sync + ?Sized,
44+
{
45+
async fn get_mut(&mut self, key: &DbKey) -> PatriciaStorageResult<Option<DbValue>> {
46+
Ok(match self.overlay.get(key).await? {
47+
Some(v) => Some(v),
48+
None => self.base.get(key).await?,
49+
})
50+
}
51+
52+
async fn mget_mut(&mut self, keys: &[&DbKey]) -> PatriciaStorageResult<Vec<Option<DbValue>>> {
53+
let mut out = self.overlay.mget(keys).await?;
54+
let mut miss_indices = Vec::new();
55+
let mut miss_keys = Vec::new();
56+
for (i, v) in out.iter().enumerate() {
57+
if v.is_none() {
58+
miss_indices.push(i);
59+
miss_keys.push(keys[i]);
60+
}
61+
}
62+
if !miss_keys.is_empty() {
63+
let fetched = self.base.mget(&miss_keys).await?;
64+
for (idx, val) in miss_indices.into_iter().zip(fetched) {
65+
out[idx] = val;
66+
}
67+
}
68+
Ok(out)
69+
}
70+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
use super::TwoLayerStorage;
2+
use crate::map_storage::MapStorage;
3+
use crate::storage_trait::{DbKey, DbValue, ReadOnlyStorage, Storage};
4+
5+
#[tokio::test]
6+
async fn read_falls_through_to_base() {
7+
let key = DbKey(vec![1, 2, 3]);
8+
let val = DbValue(vec![9]);
9+
let mut base = MapStorage::default();
10+
base.0.insert(key.clone(), val.clone());
11+
12+
let mut two = TwoLayerStorage::new(MapStorage::default(), &base);
13+
assert_eq!(two.get_mut(&key).await.unwrap(), Some(val));
14+
}
15+
16+
#[tokio::test]
17+
async fn overlay_shadows_base() {
18+
let key = DbKey(vec![1]);
19+
let base_val = DbValue(vec![1]);
20+
let over_val = DbValue(vec![2]);
21+
let mut base = MapStorage::default();
22+
base.0.insert(key.clone(), base_val);
23+
24+
let mut two = TwoLayerStorage::new(MapStorage::default(), &base);
25+
two.overlay.set(key.clone(), over_val.clone()).await.unwrap();
26+
assert_eq!(two.get_mut(&key).await.unwrap(), Some(over_val));
27+
}
28+
29+
#[tokio::test]
30+
async fn delete_drops_overlay_entry_and_sees_base() {
31+
let key = DbKey(vec![7]);
32+
let base_val = DbValue(vec![42]);
33+
let mut base = MapStorage::default();
34+
base.0.insert(key.clone(), base_val.clone());
35+
36+
let mut two = TwoLayerStorage::new(MapStorage::default(), &base);
37+
let overlay_val = DbValue(vec![99]);
38+
two.overlay.set(key.clone(), overlay_val).await.unwrap();
39+
assert_eq!(two.get_mut(&key).await.unwrap(), Some(overlay_val));
40+
two.overlay.delete(&key).await.unwrap();
41+
assert_eq!(two.get_mut(&key).await.unwrap(), Some(base_val));
42+
}
43+
44+
#[tokio::test]
45+
async fn mget_mut_uses_immutable_base_mget_on_miss() {
46+
let key_base_only = DbKey(vec![3]);
47+
let key_overlay = DbKey(vec![4]);
48+
let base_val = DbValue(vec![11]);
49+
let over_val = DbValue(vec![22]);
50+
let mut base = MapStorage::default();
51+
base.0.insert(key_base_only.clone(), base_val.clone());
52+
53+
let mut layered = TwoLayerStorage::new(MapStorage::default(), &base);
54+
layered.overlay.set(key_overlay.clone(), over_val.clone()).await.unwrap();
55+
56+
let keys = [&key_base_only, &key_overlay];
57+
assert_eq!(layered.mget_mut(&keys).await.unwrap(), vec![Some(base_val), Some(over_val)]);
58+
}

0 commit comments

Comments
 (0)