Skip to content

Commit 4e145e6

Browse files
committed
implement CacheKVS and wrap TxStore with it
Signed-off-by: Jun Kimura <jun.kimura@datachain.jp>
1 parent be4550a commit 4e145e6

File tree

4 files changed

+135
-6
lines changed

4 files changed

+135
-6
lines changed

enclave-modules/environment/src/environment_impl.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{prelude::*, Env};
22
use alloc::sync::Arc;
3-
use host_api::store::EnclaveStore;
3+
use host_api::store::new_enclave_store;
44
use light_client::{LightClient, LightClientResolver, MapLightClientRegistry};
55
use store::{KVStore, TxId};
66

@@ -24,7 +24,7 @@ impl LightClientResolver for Environment {
2424

2525
impl Env for Environment {
2626
fn new_store(&self, tx_id: TxId) -> Box<dyn KVStore> {
27-
Box::new(EnclaveStore::new(tx_id))
27+
new_enclave_store(tx_id)
2828
}
2929

3030
fn get_lc_registry(&self) -> Arc<dyn LightClientResolver> {

enclave-modules/host-api/src/store.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,27 @@
11
use crate::prelude::*;
22
use crate::{api::execute_command, Error};
33
use ocall_commands::{Command, CommandResult, StoreCommand, StoreResult};
4+
use store::cache::CacheKVS;
45
use store::{KVStore, TxId};
56

6-
/// EnclaveStore is a KVStore implementation that uses the ocall_commands to interact with the
7+
/// The store guarantees that reads a value from the host store only once per key
8+
pub fn new_enclave_store(tx_id: TxId) -> Box<dyn KVStore> {
9+
Box::new(CacheKVS::new(TxStore::new(tx_id)))
10+
}
11+
12+
/// TxStore is a KVStore implementation that uses the ocall_commands to interact with the
713
/// host's store.
8-
pub struct EnclaveStore {
14+
struct TxStore {
915
tx_id: TxId,
1016
}
1117

12-
impl EnclaveStore {
18+
impl TxStore {
1319
pub fn new(tx_id: TxId) -> Self {
1420
Self { tx_id }
1521
}
1622
}
1723

18-
impl KVStore for EnclaveStore {
24+
impl KVStore for TxStore {
1925
fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
2026
get(self.tx_id, key.to_vec()).unwrap()
2127
}

modules/store/src/cache.rs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
use crate::prelude::*;
2+
use crate::KVStore;
3+
use alloc::collections::BTreeMap;
4+
use core::cell::RefCell;
5+
6+
/// A key-value store that caches reads in memory
7+
pub struct CacheKVS<S: KVStore> {
8+
parent: S,
9+
cache: RefCell<BTreeMap<Vec<u8>, Option<Vec<u8>>>>,
10+
}
11+
12+
impl<S: KVStore> CacheKVS<S> {
13+
pub fn new(parent: S) -> Self {
14+
Self {
15+
parent,
16+
cache: RefCell::new(BTreeMap::new()),
17+
}
18+
}
19+
}
20+
21+
impl<S: KVStore> KVStore for CacheKVS<S> {
22+
fn set(&mut self, key: Vec<u8>, value: Vec<u8>) {
23+
self.parent.set(key.clone(), value.clone());
24+
self.cache.borrow_mut().insert(key, Some(value));
25+
}
26+
27+
fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
28+
let cache = self.cache.borrow();
29+
let res = cache.get(key);
30+
match res {
31+
Some(Some(v)) => Some(v.clone()),
32+
Some(None) => None,
33+
None => {
34+
drop(cache);
35+
let v = self.parent.get(key);
36+
self.cache.borrow_mut().insert(key.to_vec(), v.clone());
37+
v
38+
}
39+
}
40+
}
41+
42+
fn remove(&mut self, key: &[u8]) {
43+
self.parent.remove(key);
44+
self.cache.borrow_mut().insert(key.to_vec(), None);
45+
}
46+
}
47+
48+
#[cfg(test)]
49+
mod tests {
50+
use super::*;
51+
use crate::KVStore;
52+
use alloc::rc::Rc;
53+
54+
pub struct MockStore {
55+
db: BTreeMap<Vec<u8>, Vec<u8>>,
56+
}
57+
58+
impl MockStore {
59+
pub fn new() -> Self {
60+
Self {
61+
db: BTreeMap::new(),
62+
}
63+
}
64+
}
65+
66+
impl KVStore for MockStore {
67+
fn set(&mut self, key: Vec<u8>, value: Vec<u8>) {
68+
self.db.insert(key, value);
69+
}
70+
71+
fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
72+
self.db.get(key).cloned()
73+
}
74+
75+
fn remove(&mut self, key: &[u8]) {
76+
self.db.remove(key);
77+
}
78+
}
79+
80+
#[allow(non_snake_case)]
81+
fn B(s: &str) -> Vec<u8> {
82+
s.as_bytes().to_vec()
83+
}
84+
85+
#[test]
86+
fn test_cache_kvs() {
87+
let mut mock = Rc::new(RefCell::new(MockStore::new()));
88+
mock.set(B("k1"), B("v1"));
89+
90+
let mut cache = CacheKVS::new(mock.clone());
91+
assert_eq!(cache.get(&B("k1")), Some(B("v1")));
92+
93+
cache.set(B("k1"), B("v2"));
94+
assert_eq!(cache.get(&B("k1")), Some(B("v2")));
95+
assert_eq!(mock.get(&B("k1")), Some(B("v2")));
96+
97+
mock.set(B("k1"), B("v3"));
98+
assert_eq!(mock.get(&B("k1")), Some(B("v3")));
99+
assert_eq!(cache.get(&B("k1")), Some(B("v2")));
100+
101+
mock.remove(&B("k1"));
102+
assert_eq!(mock.get(&B("k1")), None);
103+
assert_eq!(cache.get(&B("k1")), Some(B("v2")));
104+
105+
mock.set(B("k2"), B("v4"));
106+
assert_eq!(cache.get(&B("k2")), Some(B("v4")));
107+
108+
assert_eq!(cache.get(&B("k3")), None);
109+
mock.set(B("k3"), B("v5"));
110+
assert_eq!(mock.get(&B("k3")), Some(B("v5")));
111+
assert_eq!(cache.get(&B("k3")), None);
112+
cache.set(B("k3"), B("v6"));
113+
assert_eq!(cache.get(&B("k3")), Some(B("v6")));
114+
115+
cache.remove(&B("k4"));
116+
mock.set(B("k4"), B("v7"));
117+
assert_eq!(cache.get(&B("k4")), None);
118+
assert_eq!(mock.get(&B("k4")), Some(B("v7")));
119+
cache.set(B("k4"), B("v8"));
120+
assert_eq!(cache.get(&B("k4")), Some(B("v8")));
121+
}
122+
}

modules/store/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ mod prelude {
2222
pub use crate::errors::{Error, Result};
2323
pub use crate::store::{KVStore, TxId};
2424

25+
pub mod cache;
2526
mod errors;
2627
#[cfg(feature = "std")]
2728
pub mod host;

0 commit comments

Comments
 (0)