16
16
17
17
use super :: * ;
18
18
19
+ use indexmap:: IndexMap ;
20
+ use rocksdb:: WriteBatch ;
19
21
use snarkvm:: synthesizer:: store:: helpers:: { Map , MapRead } ;
20
22
21
23
use core:: { fmt, fmt:: Debug , hash:: Hash } ;
@@ -25,7 +27,10 @@ use std::{borrow::Cow, sync::atomic::Ordering};
25
27
pub struct DataMap < K : Serialize + DeserializeOwned , V : Serialize + DeserializeOwned > {
26
28
pub ( super ) database : RocksDB ,
27
29
pub ( super ) context : Vec < u8 > ,
28
- pub ( super ) _phantom : PhantomData < ( K , V ) > ,
30
+ /// The tracker for whether a database transaction is in progress.
31
+ pub ( super ) batch_in_progress : Arc < AtomicBool > ,
32
+ /// The database transaction.
33
+ pub ( super ) atomic_batch : Arc < Mutex < IndexMap < K , Option < V > > > > ,
29
34
}
30
35
31
36
impl <
@@ -38,18 +43,19 @@ impl<
38
43
/// Inserts the given key-value pair into the map.
39
44
///
40
45
fn insert ( & self , key : K , value : V ) -> Result < ( ) > {
41
- // Prepare the prefixed key and serialized value.
42
- let raw_key = self . create_prefixed_key ( & key) ?;
43
- let raw_value = bincode:: serialize ( & value) ?;
44
-
45
46
// Determine if an atomic batch is in progress.
46
- let is_batch = self . database . batch_in_progress . load ( Ordering :: SeqCst ) ;
47
+ let is_batch = self . batch_in_progress . load ( Ordering :: SeqCst ) ;
47
48
48
49
match is_batch {
49
50
// If a batch is in progress, add the key-value pair to the batch.
50
- true => self . database . atomic_batch . lock ( ) . put ( raw_key, raw_value) ,
51
+ true => {
52
+ self . atomic_batch . lock ( ) . insert ( key, Some ( value) ) ;
53
+ }
51
54
// Otherwise, insert the key-value pair directly into the map.
52
55
false => {
56
+ // Prepare the prefixed key and serialized value.
57
+ let raw_key = self . create_prefixed_key ( & key) ?;
58
+ let raw_value = bincode:: serialize ( & value) ?;
53
59
self . database . put ( & raw_key, & raw_value) ?;
54
60
}
55
61
}
@@ -61,17 +67,18 @@ impl<
61
67
/// Removes the key-value pair for the given key from the map.
62
68
///
63
69
fn remove ( & self , key : & K ) -> Result < ( ) > {
64
- // Prepare the prefixed key.
65
- let raw_key = self . create_prefixed_key ( key) ?;
66
-
67
70
// Determine if an atomic batch is in progress.
68
- let is_batch = self . database . batch_in_progress . load ( Ordering :: SeqCst ) ;
71
+ let is_batch = self . batch_in_progress . load ( Ordering :: SeqCst ) ;
69
72
70
73
match is_batch {
71
74
// If a batch is in progress, add the key to the batch.
72
- true => self . database . atomic_batch . lock ( ) . delete ( raw_key) ,
75
+ true => {
76
+ self . atomic_batch . lock ( ) . insert ( * key, None ) ;
77
+ }
73
78
// Otherwise, remove the key-value pair directly from the map.
74
79
false => {
80
+ // Prepare the prefixed key.
81
+ let raw_key = self . create_prefixed_key ( key) ?;
75
82
self . database . delete ( & raw_key) ?;
76
83
}
77
84
}
85
92
///
86
93
fn start_atomic ( & self ) {
87
94
// Set the atomic batch flag to `true`.
88
- self . database . batch_in_progress . store ( true , Ordering :: SeqCst ) ;
95
+ self . batch_in_progress . store ( true , Ordering :: SeqCst ) ;
89
96
// Ensure that the atomic batch is empty.
90
- assert ! ( self . database . atomic_batch. lock( ) . is_empty( ) ) ;
97
+ assert ! ( self . atomic_batch. lock( ) . is_empty( ) ) ;
91
98
}
92
99
93
100
///
@@ -96,33 +103,50 @@ impl<
96
103
/// if they are already part of a larger one.
97
104
///
98
105
fn is_atomic_in_progress ( & self ) -> bool {
99
- self . database . batch_in_progress . load ( Ordering :: SeqCst )
106
+ self . batch_in_progress . load ( Ordering :: SeqCst )
100
107
}
101
108
102
109
///
103
110
/// Aborts the current atomic operation.
104
111
///
105
112
fn abort_atomic ( & self ) {
106
113
// Clear the atomic batch.
107
- self . database . atomic_batch . lock ( ) . clear ( ) ;
114
+ * self . atomic_batch . lock ( ) = Default :: default ( ) ;
108
115
// Set the atomic batch flag to `false`.
109
- self . database . batch_in_progress . store ( false , Ordering :: SeqCst ) ;
116
+ self . batch_in_progress . store ( false , Ordering :: SeqCst ) ;
110
117
}
111
118
112
119
///
113
120
/// Finishes an atomic operation, performing all the queued writes.
114
121
///
115
122
fn finish_atomic ( & self ) -> Result < ( ) > {
116
123
// Retrieve the atomic batch.
117
- let batch = core:: mem:: take ( & mut * self . database . atomic_batch . lock ( ) ) ;
118
-
119
- if !batch. is_empty ( ) {
120
- // Execute the batch of operations atomically.
124
+ let operations = core:: mem:: take ( & mut * self . atomic_batch . lock ( ) ) ;
125
+
126
+ if !operations. is_empty ( ) {
127
+ // Prepare operations batch for underlying database.
128
+ let mut batch = WriteBatch :: default ( ) ;
129
+ for operation in operations {
130
+ match operation {
131
+ ( key, Some ( value) ) => {
132
+ // Prepare the prefixed key and serialized value for insertion.
133
+ let raw_key = self . create_prefixed_key ( & key) ?;
134
+ let raw_value = bincode:: serialize ( & value) ?;
135
+ batch. put ( raw_key, raw_value) ;
136
+ }
137
+ ( key, None ) => {
138
+ // Prepare the prefixed key for deletion.
139
+ let raw_key = self . create_prefixed_key ( & key) ?;
140
+ batch. delete ( raw_key) ;
141
+ }
142
+ } ;
143
+ }
144
+ // Execute all the operations atomically.
121
145
self . database . rocksdb . write ( batch) ?;
122
146
}
123
147
124
148
// Set the atomic batch flag to `false`.
125
- self . database . batch_in_progress . store ( false , Ordering :: SeqCst ) ;
149
+ self . batch_in_progress . store ( false , Ordering :: SeqCst ) ;
126
150
127
151
Ok ( ( ) )
128
152
}
@@ -177,60 +201,7 @@ impl<
177
201
K : Borrow < Q > ,
178
202
Q : PartialEq + Eq + Hash + Serialize + ?Sized ,
179
203
{
180
- // Return early if there is no atomic batch in progress.
181
- if self . database . batch_in_progress . load ( Ordering :: SeqCst ) {
182
- struct OperationFinder {
183
- key : Vec < u8 > ,
184
- value : Option < Option < Box < [ u8 ] > > > ,
185
- }
186
-
187
- impl rocksdb:: WriteBatchIterator for OperationFinder {
188
- fn put ( & mut self , key : Box < [ u8 ] > , value : Box < [ u8 ] > ) {
189
- if * key == self . key {
190
- self . value = Some ( Some ( value) ) ;
191
- }
192
- }
193
-
194
- fn delete ( & mut self , key : Box < [ u8 ] > ) {
195
- if * key == self . key {
196
- self . value = Some ( None ) ;
197
- }
198
- }
199
- }
200
-
201
- // Prepare the prefixed key and serialized value.
202
- let raw_key = match self . create_prefixed_key ( key) {
203
- Ok ( key) => key,
204
- Err ( error) => {
205
- error ! ( "Failed to create prefixed key in 'get_batched': {:?}" , error) ;
206
- return None ;
207
- }
208
- } ;
209
-
210
- // Retrieve the atomic batch.
211
- let batch = self . database . atomic_batch . lock ( ) ;
212
-
213
- // Initialize the operation finder.
214
- let mut finder = OperationFinder { key : raw_key, value : None } ;
215
-
216
- // Iterate over the batch.
217
- batch. iterate ( & mut finder) ;
218
-
219
- // Return the value.
220
- match finder. value {
221
- Some ( Some ( value) ) => match bincode:: deserialize ( & value) {
222
- Ok ( value) => Some ( Some ( value) ) ,
223
- Err ( error) => {
224
- error ! ( "Failed to deserialize value in 'get_batched': {:?}" , error) ;
225
- None
226
- }
227
- } ,
228
- Some ( None ) => Some ( None ) ,
229
- None => None ,
230
- }
231
- } else {
232
- None
233
- }
204
+ if self . batch_in_progress . load ( Ordering :: SeqCst ) { self . atomic_batch . lock ( ) . get ( key) . cloned ( ) } else { None }
234
205
}
235
206
236
207
///
0 commit comments