@@ -2,42 +2,71 @@ use crate::errors::Result;
2
2
use crate :: mock:: MockStorage ;
3
3
use crate :: traits:: { Api , Extern , ReadonlyStorage , Storage } ;
4
4
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 ,
8
8
/// these are local changes not flushed to backing storage
9
9
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 {
10
15
/// 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
+ }
12
35
}
13
36
14
37
enum Op {
38
+ /// represents the `Set` operation for setting a key-value pair in storage
15
39
Set { key : Vec < u8 > , value : Vec < u8 > } ,
16
40
}
17
41
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 {
20
53
StorageTransaction {
21
54
storage,
22
55
local_state : MockStorage :: new ( ) ,
23
- rep_log : vec ! [ ] ,
56
+ rep_log : RepLog :: new ( ) ,
24
57
}
25
58
}
26
59
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
34
63
}
35
64
36
65
/// rollback will consume the checkpoint and drop all changes (no really needed, going out of scope does the same, but nice for clarity)
37
66
pub fn rollback ( self ) { }
38
67
}
39
68
40
- impl < ' a , S : Storage > ReadonlyStorage for StorageTransaction < ' a , S > {
69
+ impl < ' a , S : ReadonlyStorage > ReadonlyStorage for StorageTransaction < ' a , S > {
41
70
fn get ( & self , key : & [ u8 ] ) -> Option < Vec < u8 > > {
42
71
match self . local_state . get ( key) {
43
72
Some ( val) => Some ( val) ,
@@ -46,40 +75,41 @@ impl<'a, S: Storage> ReadonlyStorage for StorageTransaction<'a, S> {
46
75
}
47
76
}
48
77
49
- impl < ' a , S : Storage > Storage for StorageTransaction < ' a , S > {
78
+ impl < ' a , S : ReadonlyStorage > Storage for StorageTransaction < ' a , S > {
50
79
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 {
53
81
key : key. to_vec ( ) ,
54
82
value : value. to_vec ( ) ,
55
- } )
83
+ } ;
84
+ op. apply ( & mut self . local_state ) ;
85
+ self . rep_log . append ( op) ;
56
86
}
57
87
}
58
88
59
89
pub fn transactional < S : Storage , T > (
60
90
storage : & mut S ,
61
91
tx : & dyn Fn ( & mut StorageTransaction < S > ) -> Result < T > ,
62
92
) -> 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 ) ;
66
96
Ok ( res)
67
97
}
68
98
69
99
pub fn transactional_deps < S : Storage , A : Api , T > (
70
100
deps : & mut Extern < S , A > ,
71
101
tx : & dyn Fn ( & mut Extern < StorageTransaction < S > , A > ) -> Result < T > ,
72
102
) -> 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 {
75
105
storage : c,
76
106
api : deps. api ,
77
107
} ;
78
- let res = tx ( & mut deps ) ;
108
+ let res = tx ( & mut stx_deps ) ;
79
109
if res. is_ok ( ) {
80
- deps . storage . commit ( ) ;
110
+ stx_deps . storage . prepare ( ) . commit ( & mut deps . storage ) ;
81
111
} else {
82
- deps . storage . rollback ( ) ;
112
+ stx_deps . storage . rollback ( ) ;
83
113
}
84
114
res
85
115
}
@@ -95,11 +125,30 @@ mod test {
95
125
let mut base = MockStorage :: new ( ) ;
96
126
base. set ( b"foo" , b"bar" ) ;
97
127
98
- let mut check = StorageTransaction :: new ( & mut base) ;
128
+ let mut check = StorageTransaction :: new ( & base) ;
99
129
assert_eq ! ( check. get( b"foo" ) , Some ( b"bar" . to_vec( ) ) ) ;
100
130
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 ) ;
102
150
151
+ stxn1. prepare ( ) . commit ( & mut base) ;
103
152
assert_eq ! ( base. get( b"subtx" ) , Some ( b"works" . to_vec( ) ) ) ;
104
153
}
105
154
0 commit comments