@@ -130,6 +130,19 @@ describe('rocksdbP', () => {
130
130
await rocksdbP . iteratorClose ( iter ) ;
131
131
await rocksdbP . snapshotRelease ( snap ) ;
132
132
} ) ;
133
+ test ( 'iterators have consistent iteration' , async ( ) => {
134
+ await rocksdbP . dbPut ( db , 'K1' , '100' , { } ) ;
135
+ await rocksdbP . dbPut ( db , 'K2' , '100' , { } ) ;
136
+ const iter = rocksdbP . iteratorInit ( db , { } ) ;
137
+ expect ( await rocksdbP . iteratorNextv ( iter , 1 ) ) . toEqual (
138
+ [ [ [ 'K1' , '100' ] ] , false ]
139
+ ) ;
140
+ await rocksdbP . dbPut ( db , 'K2' , '200' , { } ) ;
141
+ expect ( await rocksdbP . iteratorNextv ( iter , 1 ) ) . toEqual (
142
+ [ [ [ 'K2' , '100' ] ] , false ]
143
+ ) ;
144
+ await rocksdbP . iteratorClose ( iter ) ;
145
+ } ) ;
133
146
} ) ;
134
147
describe ( 'transactions' , ( ) => {
135
148
test ( 'transactionCommit is idempotent' , async ( ) => {
@@ -232,6 +245,32 @@ describe('rocksdbP', () => {
232
245
expect ( await rocksdbP . transactionGet ( tran , 'K1' , { } ) ) . toBe ( '200' ) ;
233
246
await rocksdbP . transactionCommit ( tran ) ;
234
247
} ) ;
248
+ test ( 'iterator non-repeatable reads' , async ( ) => {
249
+ await rocksdbP . dbPut ( db , 'K1' , '100' , { } ) ;
250
+ await rocksdbP . dbPut ( db , 'K2' , '100' , { } ) ;
251
+ const tran = rocksdbP . transactionInit ( db , { } ) ;
252
+ await rocksdbP . dbPut ( db , 'K1' , '200' , { } ) ;
253
+ await rocksdbP . dbPut ( db , 'K2' , '200' , { } ) ;
254
+ const iter1 = rocksdbP . transactionIteratorInit ( tran , { } ) ;
255
+ expect ( await rocksdbP . iteratorNextv ( iter1 , 2 ) ) . toEqual (
256
+ [ [
257
+ [ 'K1' , '200' ] ,
258
+ [ 'K2' , '200' ] ,
259
+ ] , false ]
260
+ ) ;
261
+ await rocksdbP . iteratorClose ( iter1 ) ;
262
+ await rocksdbP . dbPut ( db , 'K1' , '300' , { } ) ;
263
+ await rocksdbP . dbPut ( db , 'K2' , '300' , { } ) ;
264
+ const iter2 = rocksdbP . transactionIteratorInit ( tran , { } ) ;
265
+ expect ( await rocksdbP . iteratorNextv ( iter2 , 2 ) ) . toEqual (
266
+ [ [
267
+ [ 'K1' , '300' ] ,
268
+ [ 'K2' , '300' ] ,
269
+ ] , false ]
270
+ ) ;
271
+ await rocksdbP . iteratorClose ( iter2 ) ;
272
+ await rocksdbP . transactionRollback ( tran ) ;
273
+ } ) ;
235
274
} ) ;
236
275
describe ( 'transaction with snapshot' , ( ) => {
237
276
test ( 'conflicts when db write occurs after snapshot creation' , async ( ) => {
@@ -263,14 +302,14 @@ describe('rocksdbP', () => {
263
302
expect ( await rocksdbP . transactionGet ( tran , 'K1' , { snapshot : tranSnap } ) ) . toBe ( '300' ) ;
264
303
await rocksdbP . transactionRollback ( tran ) ;
265
304
} ) ;
266
- test . only ( 'iterator repeatable reads' , async ( ) => {
305
+ test ( 'iterator repeatable reads' , async ( ) => {
267
306
await rocksdbP . dbPut ( db , 'K1' , '100' , { } ) ;
268
307
await rocksdbP . dbPut ( db , 'K2' , '100' , { } ) ;
269
308
const tran = rocksdbP . transactionInit ( db , { } ) ;
270
309
await rocksdbP . transactionPut ( tran , 'K3' , '100' ) ;
271
- const tranSnap = rocksdbP . transactionSnapshot ( tran ) ;
310
+ const tranSnap1 = rocksdbP . transactionSnapshot ( tran ) ;
272
311
const iter1 = rocksdbP . transactionIteratorInit ( tran , {
273
- snapshot : tranSnap
312
+ snapshot : tranSnap1
274
313
} ) ;
275
314
expect ( await rocksdbP . iteratorNextv ( iter1 , 3 ) ) . toEqual (
276
315
[ [
@@ -284,16 +323,38 @@ describe('rocksdbP', () => {
284
323
await rocksdbP . transactionPut ( tran , 'K3' , '200' ) ;
285
324
await rocksdbP . dbPut ( db , 'K1' , '200' , { } ) ;
286
325
const iter2 = rocksdbP . transactionIteratorInit ( tran , {
287
- snapshot : tranSnap
326
+ snapshot : tranSnap1
288
327
} ) ;
328
+ // Notice that this iteration uses the new values written
329
+ // to in this transaction, this mean the snapshot only applies
330
+ // to the underlying database, it's not a snapshot on the transaction
331
+ // writes
289
332
expect ( await rocksdbP . iteratorNextv ( iter2 , 3 ) ) . toEqual (
290
333
[ [
291
334
[ 'K1' , '100' ] ,
292
- [ 'K2' , '100 ' ] ,
293
- [ 'K3' , '100 ' ] ,
335
+ [ 'K2' , '200 ' ] ,
336
+ [ 'K3' , '200 ' ] ,
294
337
] , false ]
295
338
) ;
296
339
await rocksdbP . iteratorClose ( iter2 ) ;
340
+ // Resetting the snapshot for the transaction
341
+ // Now the snapshot takes the current state of the DB,
342
+ // but the transaction writes are overlayed on top
343
+ const tranSnap2 = rocksdbP . transactionSnapshot ( tran ) ;
344
+ await rocksdbP . dbPut ( db , 'K2' , '300' , { } ) ;
345
+ const iter3 = rocksdbP . transactionIteratorInit ( tran , {
346
+ snapshot : tranSnap2
347
+ } ) ;
348
+ expect ( await rocksdbP . iteratorNextv ( iter3 , 3 ) ) . toEqual (
349
+ [ [
350
+ [ 'K1' , '200' ] ,
351
+ [ 'K2' , '200' ] ,
352
+ [ 'K3' , '200' ] ,
353
+ ] , false ]
354
+ ) ;
355
+ await rocksdbP . iteratorClose ( iter3 ) ;
356
+ // Therefore iterators should always use the snapshot taken
357
+ // at the beginning of the transaction
297
358
await rocksdbP . transactionRollback ( tran ) ;
298
359
} ) ;
299
360
} ) ;
0 commit comments