@@ -140,9 +140,6 @@ open(Dirname, Opts) ->
140
140
{error , Reason }
141
141
end .
142
142
143
-
144
-
145
-
146
143
% % @doc Close a bitcask data store and flush any pending writes to disk.
147
144
-spec close (reference ()) -> ok .
148
145
close (Ref ) ->
@@ -452,34 +449,39 @@ iterator_release(Ref) ->
452
449
% % into a more compact form.
453
450
-spec merge (Dirname :: string ()) -> ok .
454
451
merge (Dirname ) ->
455
- merge (Dirname , [], readable_files (Dirname )).
452
+ merge (Dirname , [], { readable_files (Dirname ), []} ).
456
453
457
454
% % @doc Merge several data files within a bitcask datastore
458
455
% % into a more compact form.
459
456
-spec merge (Dirname :: string (), Opts :: [_ ]) -> ok .
460
457
merge (Dirname , Opts ) ->
461
- merge (Dirname , Opts , readable_files (Dirname )).
458
+ merge (Dirname , Opts , { readable_files (Dirname ), []} ).
462
459
463
460
% % @doc Merge several data files within a bitcask datastore
464
461
% % into a more compact form.
465
462
-spec merge (Dirname :: string (), Opts :: [_ ], FilesToMerge :: [string ()]) -> ok .
466
463
merge (_Dirname , _Opts , []) ->
467
464
ok ;
468
- merge (Dirname , Opts , FilesToMerge0 ) ->
465
+ merge (Dirname ,Opts ,FilesToMerge ) when is_list (FilesToMerge ) ->
466
+ merge (Dirname ,Opts ,{FilesToMerge ,[]});
467
+ merge (_Dirname , _Opts , {[],_ }) ->
468
+ ok ;
469
+ merge (Dirname , Opts , {FilesToMerge0 , ExpiredFiles0 }) ->
469
470
% % Make sure bitcask app is started so we can pull defaults from env
470
471
ok = start_app (),
471
-
472
472
% % Filter the files to merge and ensure that they all exist. It's
473
473
% % possible in some circumstances that we'll get an out-of-date
474
474
% % list of files.
475
475
FilesToMerge = [F || F <- FilesToMerge0 ,
476
476
filelib :is_file (F )],
477
- merge1 (Dirname , Opts , FilesToMerge ).
477
+ ExpiredFiles = [F || F <- ExpiredFiles0 ,
478
+ filelib :is_file (F )],
479
+ merge1 (Dirname , Opts , FilesToMerge , ExpiredFiles ).
478
480
479
481
% % Inner merge function, assumes that bitcask is running and all files exist.
480
- merge1 (_Dirname , _Opts , []) ->
482
+ merge1 (_Dirname , _Opts , [], [] ) ->
481
483
ok ;
482
- merge1 (Dirname , Opts , FilesToMerge ) ->
484
+ merge1 (Dirname , Opts , FilesToMerge , ExpiredFiles ) ->
483
485
% % Test to see if this is a complete or partial merge
484
486
Partial = not (lists :usort (readable_files (Dirname )) ==
485
487
lists :usort (FilesToMerge )),
@@ -550,18 +552,22 @@ merge1(Dirname, Opts, FilesToMerge) ->
550
552
TooNew = [F # file_status .filename ||
551
553
F <- Summary ,
552
554
F # file_status .newest_tstamp >= MergeStart ],
553
- InFiles = lists :reverse (
554
- lists :foldl (fun (F , Acc ) ->
555
+ {InFiles ,InExpiredFiles } = lists :foldl (fun (F , {InFilesAcc ,InExpiredAcc } = Acc ) ->
555
556
case lists :member (F # filestate .filename ,
556
557
TooNew ) of
557
558
false ->
558
- [F |Acc ];
559
+ case lists :member (F # filestate .filename ,
560
+ ExpiredFiles ) of
561
+ false ->
562
+ {[F |InFilesAcc ],InExpiredAcc };
563
+ true ->
564
+ {InFilesAcc ,[F |InExpiredAcc ]}
565
+ end ;
559
566
true ->
560
567
bitcask_fileops :close (F ),
561
568
Acc
562
569
end
563
- end , [], InFiles1 )),
564
-
570
+ end , {[],[]}, InFiles1 ),
565
571
% % Setup our first output merge file and update the merge lock accordingly
566
572
{ok , Outfile } = bitcask_fileops :create_file (Dirname , Opts ),
567
573
ok = bitcask_lockops :write_activefile (
@@ -585,6 +591,7 @@ merge1(Dirname, Opts, FilesToMerge) ->
585
591
opts = Opts },
586
592
587
593
% % Finally, start the merge process
594
+ ExpiredFilesFinished = expiry_merge (InExpiredFiles , LiveKeyDir , []),
588
595
State1 = merge_files (State ),
589
596
590
597
% % Make sure to close the final output file
@@ -593,11 +600,16 @@ merge1(Dirname, Opts, FilesToMerge) ->
593
600
594
601
% % Close the original input files, schedule them for deletion,
595
602
% % close keydirs, and release our lock
596
- [bitcask_fileops :close (F ) || F <- State # mstate .input_files ],
603
+ [bitcask_fileops :close (F ) || F <- State # mstate .input_files ++ ExpiredFilesFinished ],
597
604
{_ , _ , _ , {IterGeneration , _ , _ }} = bitcask_nifs :keydir_info (LiveKeyDir ),
598
- FileNames = [F # filestate .filename || F <- State # mstate .input_files ],
605
+ FileNames = [F # filestate .filename || F <- State # mstate .input_files ++ ExpiredFilesFinished ],
599
606
[catch set_setuid_bit (F ) || F <- FileNames ],
600
607
bitcask_merge_delete :defer_delete (Dirname , IterGeneration , FileNames ),
608
+ if InFiles == [] ->
609
+ bitcask_fileops :delete (Outfile );
610
+ true ->
611
+ ok
612
+ end ,
601
613
602
614
% % Explicitly release our keydirs instead of waiting for GC
603
615
bitcask_nifs :keydir_release (LiveKeyDir ),
@@ -610,8 +622,8 @@ consider_for_merge(FragTrigger, DeadBytesTrigger, ExpirationGraceTime) ->
610
622
fun (F ) ->
611
623
(F # file_status .fragmented >= FragTrigger )
612
624
orelse (F # file_status .dead_bytes >= DeadBytesTrigger )
613
- orelse ( (F # file_status .oldest_tstamp > 0 ) % % means that the file has data
614
- andalso (F # file_status .oldest_tstamp < ExpirationGraceTime )
625
+ orelse ((F # file_status .oldest_tstamp > 0 ) andalso % % means that the file has data
626
+ (F # file_status .newest_tstamp < ExpirationGraceTime )
615
627
)
616
628
end .
617
629
@@ -684,9 +696,17 @@ needs_merge(Ref) ->
684
696
_ ->
685
697
ok
686
698
end ,
687
-
688
699
FileNames = [Filename || {Filename , _Reasons } <- MergableFiles ],
689
- {true , FileNames };
700
+ F = fun (X ) ->
701
+ case X of
702
+ {data_expired ,_ ,_ } ->
703
+ true ;
704
+ _ ->
705
+ false
706
+ end
707
+ end ,
708
+ ExpiredFiles = [Filename || {Filename , Reasons } <- MergableFiles , lists :any (F ,Reasons )],
709
+ {true , {FileNames , ExpiredFiles }};
690
710
false ->
691
711
false
692
712
end .
@@ -732,8 +752,8 @@ small_file_threshold(Opts) ->
732
752
733
753
expired_threshold (Cutoff ) ->
734
754
fun (F ) ->
735
- if F # file_status .oldest_tstamp < Cutoff ->
736
- [{oldest_tstamp , F # file_status .oldest_tstamp , Cutoff }];
755
+ if F # file_status .newest_tstamp < Cutoff ->
756
+ [{data_expired , F # file_status .newest_tstamp , Cutoff }];
737
757
true ->
738
758
[]
739
759
end
@@ -1291,6 +1311,26 @@ poll_deferred_delete_queue_empty() ->
1291
1311
_ -> receive after 1100 -> poll_deferred_delete_queue_empty () end
1292
1312
end .
1293
1313
1314
+ % % Internal merge function for cache_merge functionality.
1315
+ expiry_merge ([], _LiveKeyDir , Acc ) ->
1316
+ Acc ;
1317
+
1318
+ expiry_merge ([File | Files ], LiveKeyDir , Acc0 ) ->
1319
+ FileId = bitcask_fileops :file_tstamp (File ),
1320
+ Fun = fun (K , Tstamp , {Offset , _TotalSz }, Acc ) ->
1321
+ bitcask_nifs :keydir_remove (LiveKeyDir , K , Tstamp , FileId , Offset ),
1322
+ Acc
1323
+ end ,
1324
+ case bitcask_fileops :fold_keys (File , Fun , ok , default ) of
1325
+ {error , Reason } ->
1326
+ error_logger :error_msg (" Error folding keys for ~p : ~p \n " , [File # filestate .filename ,Reason ]),
1327
+ Acc = Acc0 ;
1328
+ _ ->
1329
+ error_logger :info_msg (" All keys expired in: ~p scheduling file for deletion\n " , [File # filestate .filename ]),
1330
+ Acc = lists :append (Acc0 , [File ])
1331
+ end ,
1332
+ expiry_merge (Files , LiveKeyDir , Acc ).
1333
+
1294
1334
% % ===================================================================
1295
1335
% % EUnit tests
1296
1336
% % ===================================================================
@@ -1700,9 +1740,9 @@ delete_partial_merge_test() ->
1700
1740
% % selective merge, hit all of the files with deletes but not
1701
1741
% % all of the ones with deleted data
1702
1742
timer :sleep (1100 ),
1703
- ok = merge (" /tmp/bc.test.pardel" ,[],lists :reverse (lists :nthtail (2 ,
1743
+ ok = merge (" /tmp/bc.test.pardel" ,[],{ lists :reverse (lists :nthtail (2 ,
1704
1744
lists :reverse (readable_files (
1705
- " /tmp/bc.test.pardel" ))))),
1745
+ " /tmp/bc.test.pardel" )))),[]} ),
1706
1746
1707
1747
% % Verify we've now only got one item left
1708
1748
B2 = bitcask :open (" /tmp/bc.test.pardel" ),
0 commit comments