Skip to content

Commit a07ac8e

Browse files
authored
Merge pull request CosmWasm#166 from dandanlen/with-commit-struct
Add intermediary commit step to storage transactions.
2 parents 24c4d71 + 3691675 commit a07ac8e

File tree

1 file changed

+78
-29
lines changed

1 file changed

+78
-29
lines changed

src/storage.rs

Lines changed: 78 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,71 @@ use crate::errors::Result;
22
use crate::mock::MockStorage;
33
use crate::traits::{Api, Extern, ReadonlyStorage, Storage};
44

5-
pub struct StorageTransaction<'a, S: Storage> {
6-
/// a backing storage that is only modified upon commit
7-
storage: &'a mut S,
5+
pub struct StorageTransaction<'a, S: ReadonlyStorage> {
6+
/// read-only access to backing storage
7+
storage: &'a S,
88
/// these are local changes not flushed to backing storage
99
local_state: MockStorage,
10+
/// a log of local changes not yet flushed to backing storage
11+
rep_log: RepLog,
12+
}
13+
14+
pub struct RepLog {
1015
/// this is a list of changes to be written to backing storage upon commit
11-
rep_log: Vec<Op>,
16+
ops_log: Vec<Op>,
17+
}
18+
19+
impl RepLog {
20+
fn new() -> Self {
21+
RepLog { ops_log: vec![] }
22+
}
23+
24+
/// appends an op to the list of changes to be applied upon commit
25+
fn append(&mut self, op: Op) {
26+
self.ops_log.push(op);
27+
}
28+
29+
/// applies the stored list of `Op`s to the provided `Storage`
30+
pub fn commit<S: Storage>(self, storage: &mut S) {
31+
for op in self.ops_log {
32+
op.apply(storage);
33+
}
34+
}
1235
}
1336

1437
enum Op {
38+
/// represents the `Set` operation for setting a key-value pair in storage
1539
Set { key: Vec<u8>, value: Vec<u8> },
1640
}
1741

18-
impl<'a, S: Storage> StorageTransaction<'a, S> {
19-
pub fn new(storage: &'a mut S) -> Self {
42+
impl Op {
43+
/// applies this `Op` to the provided storage
44+
pub fn apply<S: Storage>(&self, storage: &mut S) {
45+
match self {
46+
Op::Set { key, value } => storage.set(&key, &value),
47+
}
48+
}
49+
}
50+
51+
impl<'a, S: ReadonlyStorage> StorageTransaction<'a, S> {
52+
pub fn new(storage: &'a S) -> Self {
2053
StorageTransaction {
2154
storage,
2255
local_state: MockStorage::new(),
23-
rep_log: vec![],
56+
rep_log: RepLog::new(),
2457
}
2558
}
2659

27-
/// commit will consume the checkpoint and write all changes to the underlying store
28-
pub fn commit(self) {
29-
for op in self.rep_log.iter() {
30-
match op {
31-
Op::Set { key, value } => self.storage.set(&key, &value),
32-
}
33-
}
60+
/// prepares this transaction to be committed to storage
61+
pub fn prepare(self) -> RepLog {
62+
self.rep_log
3463
}
3564

3665
/// rollback will consume the checkpoint and drop all changes (no really needed, going out of scope does the same, but nice for clarity)
3766
pub fn rollback(self) {}
3867
}
3968

40-
impl<'a, S: Storage> ReadonlyStorage for StorageTransaction<'a, S> {
69+
impl<'a, S: ReadonlyStorage> ReadonlyStorage for StorageTransaction<'a, S> {
4170
fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
4271
match self.local_state.get(key) {
4372
Some(val) => Some(val),
@@ -46,40 +75,41 @@ impl<'a, S: Storage> ReadonlyStorage for StorageTransaction<'a, S> {
4675
}
4776
}
4877

49-
impl<'a, S: Storage> Storage for StorageTransaction<'a, S> {
78+
impl<'a, S: ReadonlyStorage> Storage for StorageTransaction<'a, S> {
5079
fn set(&mut self, key: &[u8], value: &[u8]) {
51-
self.local_state.set(key, value);
52-
self.rep_log.push(Op::Set {
80+
let op = Op::Set {
5381
key: key.to_vec(),
5482
value: value.to_vec(),
55-
})
83+
};
84+
op.apply(&mut self.local_state);
85+
self.rep_log.append(op);
5686
}
5787
}
5888

5989
pub fn transactional<S: Storage, T>(
6090
storage: &mut S,
6191
tx: &dyn Fn(&mut StorageTransaction<S>) -> Result<T>,
6292
) -> Result<T> {
63-
let mut c = StorageTransaction::new(storage);
64-
let res = tx(&mut c)?;
65-
c.commit();
93+
let mut stx = StorageTransaction::new(storage);
94+
let res = tx(&mut stx)?;
95+
stx.prepare().commit(storage);
6696
Ok(res)
6797
}
6898

6999
pub fn transactional_deps<S: Storage, A: Api, T>(
70100
deps: &mut Extern<S, A>,
71101
tx: &dyn Fn(&mut Extern<StorageTransaction<S>, A>) -> Result<T>,
72102
) -> Result<T> {
73-
let c = StorageTransaction::new(&mut deps.storage);
74-
let mut deps = Extern {
103+
let c = StorageTransaction::new(&deps.storage);
104+
let mut stx_deps = Extern {
75105
storage: c,
76106
api: deps.api,
77107
};
78-
let res = tx(&mut deps);
108+
let res = tx(&mut stx_deps);
79109
if res.is_ok() {
80-
deps.storage.commit();
110+
stx_deps.storage.prepare().commit(&mut deps.storage);
81111
} else {
82-
deps.storage.rollback();
112+
stx_deps.storage.rollback();
83113
}
84114
res
85115
}
@@ -95,11 +125,30 @@ mod test {
95125
let mut base = MockStorage::new();
96126
base.set(b"foo", b"bar");
97127

98-
let mut check = StorageTransaction::new(&mut base);
128+
let mut check = StorageTransaction::new(&base);
99129
assert_eq!(check.get(b"foo"), Some(b"bar".to_vec()));
100130
check.set(b"subtx", b"works");
101-
check.commit();
131+
check.prepare().commit(&mut base);
132+
133+
assert_eq!(base.get(b"subtx"), Some(b"works".to_vec()));
134+
}
135+
136+
#[test]
137+
fn storage_remains_readable() {
138+
let mut base = MockStorage::new();
139+
base.set(b"foo", b"bar");
140+
141+
let mut stxn1 = StorageTransaction::new(&base);
142+
143+
assert_eq!(stxn1.get(b"foo"), Some(b"bar".to_vec()));
144+
145+
stxn1.set(b"subtx", b"works");
146+
assert_eq!(stxn1.get(b"subtx"), Some(b"works".to_vec()));
147+
148+
// Can still read from base, txn is not yet committed
149+
assert_eq!(base.get(b"subtx"), None);
102150

151+
stxn1.prepare().commit(&mut base);
103152
assert_eq!(base.get(b"subtx"), Some(b"works".to_vec()));
104153
}
105154

0 commit comments

Comments
 (0)