@@ -26,6 +26,15 @@ use crate::MapApiRO;
26
26
use crate :: MapKey ;
27
27
use crate :: SeqMarked ;
28
28
29
+ /// Return the newer if it is a `not-found` record.
30
+ pub fn compact_seq_marked_pair < T > ( newer : SeqMarked < T > , older : SeqMarked < T > ) -> SeqMarked < T > {
31
+ if newer. is_not_found ( ) {
32
+ older
33
+ } else {
34
+ newer
35
+ }
36
+ }
37
+
29
38
/// Get a key from multi levels data.
30
39
///
31
40
/// Returns the first non-tombstone entry.
@@ -116,13 +125,37 @@ mod tests {
116
125
117
126
use futures_util:: TryStreamExt ;
118
127
128
+ use super :: * ;
119
129
use crate :: compact:: compacted_get;
120
130
use crate :: compact:: compacted_range;
121
131
use crate :: impls:: immutable:: Immutable ;
122
132
use crate :: impls:: level:: Level ;
123
133
use crate :: MapApi ;
124
134
use crate :: SeqMarked ;
125
135
136
+ #[ test]
137
+ fn test_compact_seq_marked_pair ( ) {
138
+ assert_eq ! (
139
+ compact_seq_marked_pair( SeqMarked :: new_normal( 1 , "a" ) , SeqMarked :: new_normal( 2 , "b" ) ) ,
140
+ SeqMarked :: new_normal( 1 , "a" )
141
+ ) ;
142
+ assert_eq ! (
143
+ compact_seq_marked_pair( SeqMarked :: new_normal( 1 , "a" ) , SeqMarked :: new_tombstone( 2 ) ) ,
144
+ SeqMarked :: new_normal( 1 , "a" )
145
+ ) ;
146
+ assert_eq ! (
147
+ compact_seq_marked_pair( SeqMarked :: new_tombstone( 1 ) , SeqMarked :: new_normal( 2 , "b" ) ) ,
148
+ SeqMarked :: new_tombstone( 1 )
149
+ ) ;
150
+ assert_eq ! (
151
+ compact_seq_marked_pair(
152
+ SeqMarked :: <( ) >:: new_tombstone( 0 ) ,
153
+ SeqMarked :: new_tombstone( 2 )
154
+ ) ,
155
+ SeqMarked :: new_tombstone( 2 )
156
+ ) ;
157
+ }
158
+
126
159
#[ tokio:: test]
127
160
async fn test_compacted_get ( ) -> anyhow:: Result < ( ) > {
128
161
let mut l0 = Level :: default ( ) ;
@@ -248,4 +281,173 @@ mod tests {
248
281
fn b ( x : impl ToString ) -> Vec < u8 > {
249
282
x. to_string ( ) . as_bytes ( ) . to_vec ( )
250
283
}
284
+
285
+ #[ test]
286
+ fn test_compact_seq_marked_pair_edge_cases ( ) {
287
+ // Not found newer should return older
288
+ assert_eq ! (
289
+ compact_seq_marked_pair(
290
+ SeqMarked :: <String >:: new_not_found( ) ,
291
+ SeqMarked :: new_normal( 5 , "older" . to_string( ) )
292
+ ) ,
293
+ SeqMarked :: new_normal( 5 , "older" . to_string( ) )
294
+ ) ;
295
+
296
+ // Not found newer with tombstone older
297
+ assert_eq ! (
298
+ compact_seq_marked_pair(
299
+ SeqMarked :: <String >:: new_not_found( ) ,
300
+ SeqMarked :: new_tombstone( 3 )
301
+ ) ,
302
+ SeqMarked :: new_tombstone( 3 )
303
+ ) ;
304
+
305
+ // Not found both
306
+ assert_eq ! (
307
+ compact_seq_marked_pair(
308
+ SeqMarked :: <String >:: new_not_found( ) ,
309
+ SeqMarked :: <String >:: new_not_found( )
310
+ ) ,
311
+ SeqMarked :: <String >:: new_not_found( )
312
+ ) ;
313
+ }
314
+
315
+ #[ tokio:: test]
316
+ async fn test_compacted_get_empty_levels ( ) -> anyhow:: Result < ( ) > {
317
+ // No levels at all
318
+ let got = compacted_get :: < String , Level , Level > ( & s ( "missing" ) , [ ] , [ ] ) . await ?;
319
+ assert ! ( got. is_not_found( ) ) ;
320
+ Ok ( ( ) )
321
+ }
322
+
323
+ #[ tokio:: test]
324
+ async fn test_compacted_get_key_not_found ( ) -> anyhow:: Result < ( ) > {
325
+ let mut l0 = Level :: default ( ) ;
326
+ l0. set ( s ( "a" ) , Some ( b ( "a" ) ) ) . await ?;
327
+
328
+ let mut l1 = l0. new_level ( ) ;
329
+ l1. set ( s ( "b" ) , Some ( b ( "b" ) ) ) . await ?;
330
+
331
+ // Key not in any level
332
+ let got = compacted_get :: < String , _ , Level > ( & s ( "missing" ) , [ & l0, & l1] , [ ] ) . await ?;
333
+ assert ! ( got. is_not_found( ) ) ;
334
+ Ok ( ( ) )
335
+ }
336
+
337
+ #[ tokio:: test]
338
+ async fn test_compacted_get_only_tombstones ( ) -> anyhow:: Result < ( ) > {
339
+ let mut l0 = Level :: default ( ) ;
340
+ l0. set ( s ( "a" ) , Some ( b ( "a" ) ) ) . await ?;
341
+
342
+ let mut l1 = l0. new_level ( ) ;
343
+ l1. set ( s ( "a" ) , None ) . await ?; // Tombstone
344
+
345
+ let mut l2 = l1. new_level ( ) ;
346
+ l2. set ( s ( "a" ) , None ) . await ?; // Another tombstone
347
+
348
+ // Should find first tombstone
349
+ let got = compacted_get :: < String , _ , Level > ( & s ( "a" ) , [ & l2, & l1] , [ ] ) . await ?;
350
+ assert_eq ! ( got, SeqMarked :: new_tombstone( 1 ) ) ;
351
+ Ok ( ( ) )
352
+ }
353
+
354
+ #[ tokio:: test]
355
+ async fn test_compacted_range_empty_levels ( ) -> anyhow:: Result < ( ) > {
356
+ // No levels at all
357
+ let got = compacted_range :: < String , _ , Level , & Level , Level > ( s ( "" ) .., None , [ ] , [ ] ) . await ?;
358
+ let got = got. try_collect :: < Vec < _ > > ( ) . await ?;
359
+ assert ! ( got. is_empty( ) ) ;
360
+ Ok ( ( ) )
361
+ }
362
+
363
+ #[ tokio:: test]
364
+ async fn test_compacted_range_single_level ( ) -> anyhow:: Result < ( ) > {
365
+ let mut l0 = Level :: default ( ) ;
366
+ l0. set ( s ( "a" ) , Some ( b ( "a" ) ) ) . await ?;
367
+ l0. set ( s ( "b" ) , Some ( b ( "b" ) ) ) . await ?;
368
+
369
+ let got = compacted_range :: < _ , _ , Level , _ , Level > ( s ( "" ) .., None , [ & l0] , [ ] ) . await ?;
370
+ let got = got. try_collect :: < Vec < _ > > ( ) . await ?;
371
+ assert_eq ! ( got, vec![
372
+ ( s( "a" ) , SeqMarked :: new_normal( 1 , b( "a" ) ) ) ,
373
+ ( s( "b" ) , SeqMarked :: new_normal( 2 , b( "b" ) ) ) ,
374
+ ] ) ;
375
+ Ok ( ( ) )
376
+ }
377
+
378
+ #[ tokio:: test]
379
+ async fn test_compacted_range_with_persisted_only ( ) -> anyhow:: Result < ( ) > {
380
+ let mut l0 = Level :: default ( ) ;
381
+ l0. set ( s ( "a" ) , Some ( b ( "a" ) ) ) . await ?;
382
+ l0. set ( s ( "b" ) , Some ( b ( "b" ) ) ) . await ?;
383
+
384
+ // Only persisted levels, no in-memory levels
385
+ let got =
386
+ compacted_range :: < String , _ , Level , & Level , & Level > ( s ( "" ) .., None , [ ] , [ & l0] ) . await ?;
387
+ let got = got. try_collect :: < Vec < _ > > ( ) . await ?;
388
+ assert_eq ! ( got, vec![
389
+ ( s( "a" ) , SeqMarked :: new_normal( 1 , b( "a" ) ) ) ,
390
+ ( s( "b" ) , SeqMarked :: new_normal( 2 , b( "b" ) ) ) ,
391
+ ] ) ;
392
+ Ok ( ( ) )
393
+ }
394
+
395
+ #[ tokio:: test]
396
+ async fn test_compacted_range_overlapping_keys ( ) -> anyhow:: Result < ( ) > {
397
+ // Test that newer versions override older ones
398
+ let mut l0 = Level :: default ( ) ;
399
+ l0. set ( s ( "a" ) , Some ( b ( "old_a" ) ) ) . await ?;
400
+ l0. set ( s ( "c" ) , Some ( b ( "c" ) ) ) . await ?;
401
+
402
+ let mut l1 = l0. new_level ( ) ;
403
+ l1. set ( s ( "a" ) , Some ( b ( "new_a" ) ) ) . await ?; // Override
404
+ l1. set ( s ( "b" ) , Some ( b ( "b" ) ) ) . await ?; // New key
405
+
406
+ let got = compacted_range :: < _ , _ , Level , _ , Level > ( s ( "" ) .., None , [ & l1, & l0] , [ ] ) . await ?;
407
+ let got = got. try_collect :: < Vec < _ > > ( ) . await ?;
408
+ assert_eq ! ( got, vec![
409
+ ( s( "a" ) , SeqMarked :: new_normal( 3 , b( "new_a" ) ) ) , /* Newer version (sequence continues from l0) */
410
+ ( s( "b" ) , SeqMarked :: new_normal( 4 , b( "b" ) ) ) ,
411
+ ( s( "c" ) , SeqMarked :: new_normal( 2 , b( "c" ) ) ) ,
412
+ ] ) ;
413
+ Ok ( ( ) )
414
+ }
415
+
416
+ #[ tokio:: test]
417
+ async fn test_compacted_range_bounded_range ( ) -> anyhow:: Result < ( ) > {
418
+ let mut l0 = Level :: default ( ) ;
419
+ l0. set ( s ( "a" ) , Some ( b ( "a" ) ) ) . await ?;
420
+ l0. set ( s ( "b" ) , Some ( b ( "b" ) ) ) . await ?;
421
+ l0. set ( s ( "c" ) , Some ( b ( "c" ) ) ) . await ?;
422
+ l0. set ( s ( "d" ) , Some ( b ( "d" ) ) ) . await ?;
423
+
424
+ // Test bounded range
425
+ let got =
426
+ compacted_range :: < _ , _ , Level , _ , Level > ( s ( "b" ) ..=s ( "c" ) , None , [ & l0] , [ ] ) . await ?;
427
+ let got = got. try_collect :: < Vec < _ > > ( ) . await ?;
428
+ assert_eq ! ( got, vec![
429
+ ( s( "b" ) , SeqMarked :: new_normal( 2 , b( "b" ) ) ) ,
430
+ ( s( "c" ) , SeqMarked :: new_normal( 3 , b( "c" ) ) ) ,
431
+ ] ) ;
432
+ Ok ( ( ) )
433
+ }
434
+
435
+ #[ tokio:: test]
436
+ async fn test_compacted_range_all_tombstones ( ) -> anyhow:: Result < ( ) > {
437
+ let mut l0 = Level :: default ( ) ;
438
+ l0. set ( s ( "a" ) , Some ( b ( "a" ) ) ) . await ?;
439
+ l0. set ( s ( "b" ) , Some ( b ( "b" ) ) ) . await ?;
440
+
441
+ let mut l1 = l0. new_level ( ) ;
442
+ l1. set ( s ( "a" ) , None ) . await ?; // Tombstone
443
+ l1. set ( s ( "b" ) , None ) . await ?; // Tombstone
444
+
445
+ let got = compacted_range :: < _ , _ , Level , _ , Level > ( s ( "" ) .., None , [ & l1] , [ ] ) . await ?;
446
+ let got = got. try_collect :: < Vec < _ > > ( ) . await ?;
447
+ assert_eq ! ( got, vec![
448
+ ( s( "a" ) , SeqMarked :: new_tombstone( 2 ) ) , /* Both tombstones get same seq from the implementation */
449
+ ( s( "b" ) , SeqMarked :: new_tombstone( 2 ) ) ,
450
+ ] ) ;
451
+ Ok ( ( ) )
452
+ }
251
453
}
0 commit comments