@@ -222,41 +222,36 @@ impl Inner {
222
222
fn collect_garbage (
223
223
& mut self ,
224
224
view : ViewNumber ,
225
- intervals : & [ RangeInclusive < u64 > ] ,
225
+ intervals : & [ RangeInclusive < ViewNumber > ] ,
226
226
) -> anyhow:: Result < ( ) > {
227
- let view_number = view. u64 ( ) ;
228
- let prune_view = view. saturating_sub ( self . view_retention ) ;
229
-
230
- let delete_files =
231
- |intervals : & [ RangeInclusive < u64 > ] , keep, dir_path : PathBuf | -> anyhow:: Result < ( ) > {
232
- if !dir_path. is_dir ( ) {
233
- return Ok ( ( ) ) ;
234
- }
227
+ let view_number = view;
228
+ let prune_view = ViewNumber :: new ( view. saturating_sub ( self . view_retention ) ) ;
229
+
230
+ let delete_files = |intervals : & [ RangeInclusive < ViewNumber > ] ,
231
+ keep,
232
+ dir_path : PathBuf |
233
+ -> anyhow:: Result < ( ) > {
234
+ if !dir_path. is_dir ( ) {
235
+ return Ok ( ( ) ) ;
236
+ }
235
237
236
- for entry in fs:: read_dir ( dir_path) ? {
237
- let entry = entry?;
238
- let path = entry. path ( ) ;
239
-
240
- if let Some ( file) = path. file_stem ( ) . and_then ( |n| n. to_str ( ) ) {
241
- if let Ok ( v) = file. parse :: < u64 > ( ) {
242
- // If the view is the anchor view, keep it no matter what.
243
- if let Some ( keep) = keep {
244
- if keep == v {
245
- continue ;
246
- }
247
- }
248
- // Otherwise, delete it if it is time to prune this view _or_ if the
249
- // given intervals, which we've already successfully processed, contain
250
- // the view; in this case we simply don't need it anymore.
251
- if v < prune_view || intervals. iter ( ) . any ( |i| i. contains ( & v) ) {
252
- fs:: remove_file ( & path) ?;
253
- }
254
- }
238
+ for ( v, path) in view_files ( dir_path) ? {
239
+ // If the view is the anchor view, keep it no matter what.
240
+ if let Some ( keep) = keep {
241
+ if keep == v {
242
+ continue ;
255
243
}
256
244
}
245
+ // Otherwise, delete it if it is time to prune this view _or_ if the given
246
+ // intervals, which we've already successfully processed, contain the view; in
247
+ // this case we simply don't need it anymore.
248
+ if v < prune_view || intervals. iter ( ) . any ( |i| i. contains ( & v) ) {
249
+ fs:: remove_file ( & path) ?;
250
+ }
251
+ }
257
252
258
- Ok ( ( ) )
259
- } ;
253
+ Ok ( ( ) )
254
+ } ;
260
255
261
256
delete_files ( intervals, None , self . da_dir_path ( ) ) ?;
262
257
delete_files ( intervals, None , self . vid_dir_path ( ) ) ?;
@@ -276,22 +271,13 @@ impl Inner {
276
271
& self ,
277
272
view : ViewNumber ,
278
273
consumer : & impl EventConsumer ,
279
- ) -> anyhow:: Result < Vec < RangeInclusive < u64 > > > {
274
+ ) -> anyhow:: Result < Vec < RangeInclusive < ViewNumber > > > {
280
275
// Generate a decide event for each leaf, to be processed by the event consumer. We make a
281
276
// separate event for each leaf because it is possible we have non-consecutive leaves in our
282
277
// storage, which would not be valid as a single decide with a single leaf chain.
283
278
let mut leaves = BTreeMap :: new ( ) ;
284
- for entry in fs:: read_dir ( self . decided_leaf_path ( ) ) ? {
285
- let entry = entry?;
286
- let path = entry. path ( ) ;
287
-
288
- let Some ( file) = path. file_stem ( ) . and_then ( |n| n. to_str ( ) ) else {
289
- continue ;
290
- } ;
291
- let Ok ( v) = file. parse :: < u64 > ( ) else {
292
- continue ;
293
- } ;
294
- if v > view. u64 ( ) {
279
+ for ( v, path) in view_files ( self . decided_leaf_path ( ) ) ? {
280
+ if v > view {
295
281
continue ;
296
282
}
297
283
@@ -302,22 +288,20 @@ impl Inner {
302
288
. context ( format ! ( "parsing decided leaf {}" , path. display( ) ) ) ?;
303
289
304
290
// Include the VID share if available.
305
- let vid_share = self
306
- . load_vid_share ( ViewNumber :: new ( v) ) ?
307
- . map ( |proposal| proposal. data ) ;
291
+ let vid_share = self . load_vid_share ( v) ?. map ( |proposal| proposal. data ) ;
308
292
if vid_share. is_none ( ) {
309
- tracing:: debug!( view = v, "VID share not available at decide" ) ;
293
+ tracing:: debug!( ? v, "VID share not available at decide" ) ;
310
294
}
311
295
312
296
// Fill in the full block payload using the DA proposals we had persisted.
313
- if let Some ( proposal) = self . load_da_proposal ( ViewNumber :: new ( v ) ) ? {
297
+ if let Some ( proposal) = self . load_da_proposal ( v ) ? {
314
298
let payload = Payload :: from_bytes (
315
299
& proposal. data . encoded_transactions ,
316
300
& proposal. data . metadata ,
317
301
) ;
318
302
leaf. fill_block_payload_unchecked ( payload) ;
319
303
} else {
320
- tracing:: debug!( view = v, "DA proposal not available at decide" ) ;
304
+ tracing:: debug!( ? v, "DA proposal not available at decide" ) ;
321
305
}
322
306
323
307
let info = LeafInfo {
@@ -339,7 +323,7 @@ impl Inner {
339
323
if let Some ( ( oldest_view, _) ) = leaves. first_key_value ( ) {
340
324
// The only exception is when the oldest leaf is the genesis leaf; then there was no
341
325
// previous decide event.
342
- if * oldest_view > 0 {
326
+ if * oldest_view > ViewNumber :: genesis ( ) {
343
327
leaves. pop_first ( ) ;
344
328
}
345
329
}
@@ -350,7 +334,7 @@ impl Inner {
350
334
let height = leaf. leaf . block_header ( ) . block_number ( ) ;
351
335
consumer
352
336
. handle_event ( & Event {
353
- view_number : ViewNumber :: new ( view) ,
337
+ view_number : view,
354
338
event : EventType :: Decide {
355
339
qc : Arc :: new ( qc. to_qc2 ( ) ) ,
356
340
leaf_chain : Arc :: new ( vec ! [ leaf] ) ,
@@ -423,15 +407,12 @@ impl Inner {
423
407
let mut anchor: Option < ( Leaf2 , QuorumCertificate2 < SeqTypes > ) > = None ;
424
408
425
409
// Return the latest decided leaf.
426
- for entry in
427
- fs:: read_dir ( self . decided_leaf_path ( ) ) . context ( "opening decided leaf directory" ) ?
428
- {
429
- let file = entry. context ( "reading decided leaf directory" ) ?. path ( ) ;
410
+ for ( _, path) in view_files ( self . decided_leaf_path ( ) ) ? {
430
411
let bytes =
431
- fs:: read ( & file ) . context ( format ! ( "reading decided leaf {}" , file . display( ) ) ) ?;
412
+ fs:: read ( & path ) . context ( format ! ( "reading decided leaf {}" , path . display( ) ) ) ?;
432
413
let ( leaf, qc) =
433
414
bincode:: deserialize :: < ( Leaf , QuorumCertificate < SeqTypes > ) > ( & bytes)
434
- . context ( format ! ( "parsing decided leaf {}" , file . display( ) ) ) ?;
415
+ . context ( format ! ( "parsing decided leaf {}" , path . display( ) ) ) ?;
435
416
if let Some ( ( anchor_leaf, _) ) = & anchor {
436
417
if leaf. view_number ( ) > anchor_leaf. view_number ( ) {
437
418
let leaf2 = leaf. into ( ) ;
@@ -760,47 +741,10 @@ impl SequencerPersistence for Persistence {
760
741
return Ok ( Default :: default ( ) ) ;
761
742
}
762
743
763
- // Then, we want to get the entries in this directory since they'll be the
764
- // key/value pairs for our map.
765
- let files = fs:: read_dir ( dir_path. clone ( ) ) ?. filter_map ( |entry| {
766
- let entry = entry. ok ( ) ?;
767
- if entry. file_type ( ) . ok ( ) ?. is_file ( ) && entry. path ( ) . extension ( ) ? == "txt" {
768
- Some ( entry. path ( ) )
769
- } else {
770
- None
771
- }
772
- } ) ;
773
-
744
+ // Read quorum proposals from every data file in this directory.
774
745
let mut map = BTreeMap :: new ( ) ;
775
- for file in files {
776
- // Parse each file into a proposal if possible. We ignore files we don't recognize or
777
- // can't parse, as sometimes we can end up with random extra files (e.g. swap files) in
778
- // the directory.
779
- //
780
- // Get the stem to remove the ".txt" from the end.
781
- let Some ( file_name) = file. file_stem ( ) else {
782
- continue ;
783
- } ;
784
-
785
- // Parse the filename (which corresponds to the view)
786
- let file_name = file_name. to_string_lossy ( ) ;
787
- let Ok ( view_number) = file_name. parse :: < u64 > ( ) else {
788
- tracing:: info!(
789
- %file_name,
790
- "ignoring extraneous file in quorum proposals directory"
791
- ) ;
792
- continue ;
793
- } ;
794
-
795
- // Now, we'll try and load the proposal associated with this function. In this case we
796
- // do propagate errors: errors from the file system are more likely to be some transient
797
- // issue (e.g. failure to connect to a network-mounted file system) than an issue with
798
- // the file itself (like a swap file being left over in the directory). Thus, this file
799
- // likely does have good data even if we can't read it, so we do want to propagate the
800
- // error and eventually retry.
801
- let proposal_bytes = fs:: read ( file) ?;
802
-
803
- // Then, deserialize.
746
+ for ( view, path) in view_files ( & dir_path) ? {
747
+ let proposal_bytes = fs:: read ( path) ?;
804
748
let proposal: Proposal < SeqTypes , QuorumProposal < SeqTypes > > =
805
749
match bincode:: deserialize ( & proposal_bytes) {
806
750
Ok ( proposal) => proposal,
@@ -811,17 +755,14 @@ impl SequencerPersistence for Persistence {
811
755
// many proposals as we can rather than letting one bad proposal cause the
812
756
// entire operation to fail, and it is still possible that this was just
813
757
// some unintended file whose name happened to match the naming convention.
814
- tracing:: warn!(
815
- view_number,
816
- "ignoring malformed quorum proposal file: {err:#}"
817
- ) ;
758
+ tracing:: warn!( ?view, "ignoring malformed quorum proposal file: {err:#}" ) ;
818
759
continue ;
819
760
}
820
761
} ;
821
762
let proposal2 = convert_proposal ( proposal) ;
822
763
823
764
// Push to the map and we're done.
824
- map. insert ( ViewNumber :: new ( view_number ) , proposal2) ;
765
+ map. insert ( view , proposal2) ;
825
766
}
826
767
827
768
Ok ( map)
@@ -987,6 +928,31 @@ fn migrate_network_config(
987
928
Ok ( network_config)
988
929
}
989
930
931
+ /// Get all paths under `dir` whose name is of the form <view number>.txt.
932
+ fn view_files (
933
+ dir : impl AsRef < Path > ,
934
+ ) -> anyhow:: Result < impl Iterator < Item = ( ViewNumber , PathBuf ) > > {
935
+ Ok ( fs:: read_dir ( dir. as_ref ( ) ) ?. filter_map ( move |entry| {
936
+ let dir = dir. as_ref ( ) . display ( ) ;
937
+ let entry = entry. ok ( ) ?;
938
+ if !entry. file_type ( ) . ok ( ) ?. is_file ( ) {
939
+ tracing:: debug!( %dir, ?entry, "ignoring non-file in data directory" ) ;
940
+ return None ;
941
+ }
942
+ let path = entry. path ( ) ;
943
+ if path. extension ( ) ? != "txt" {
944
+ tracing:: debug!( %dir, ?entry, "ignoring non-text file in data directory" ) ;
945
+ return None ;
946
+ }
947
+ let file_name = path. file_stem ( ) ?;
948
+ let Ok ( view_number) = file_name. to_string_lossy ( ) . parse :: < u64 > ( ) else {
949
+ tracing:: debug!( %dir, ?file_name, "ignoring extraneous file in data directory" ) ;
950
+ return None ;
951
+ } ;
952
+ Some ( ( ViewNumber :: new ( view_number) , entry. path ( ) . to_owned ( ) ) )
953
+ } ) )
954
+ }
955
+
990
956
#[ cfg( test) ]
991
957
mod testing {
992
958
use tempfile:: TempDir ;
0 commit comments