@@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
7
7
use slog:: { debug, error, info, trace, warn, Logger } ;
8
8
use std:: collections:: { HashMap , HashSet } ;
9
9
use std:: mem;
10
- use std:: sync:: { mpsc , Arc } ;
10
+ use std:: sync:: Arc ;
11
11
use std:: thread;
12
12
use std:: time:: { Duration , SystemTime , UNIX_EPOCH } ;
13
13
use store:: hot_cold_store:: { migrate_database, HotColdDBError } ;
@@ -25,6 +25,7 @@ const MAX_COMPACTION_PERIOD_SECONDS: u64 = 604800;
25
25
const MIN_COMPACTION_PERIOD_SECONDS : u64 = 7200 ;
26
26
/// Compact after a large finality gap, if we respect `MIN_COMPACTION_PERIOD_SECONDS`.
27
27
const COMPACTION_FINALITY_DISTANCE : u64 = 1024 ;
28
+ const BLOCKS_PER_RECONSTRUCTION : usize = 8192 * 4 ;
28
29
29
30
/// Default number of epochs to wait between finalization migrations.
30
31
pub const DEFAULT_EPOCHS_PER_RUN : u64 = 4 ;
@@ -33,10 +34,14 @@ pub const DEFAULT_EPOCHS_PER_RUN: u64 = 4;
33
34
/// to the cold database.
34
35
pub struct BackgroundMigrator < E : EthSpec , Hot : ItemStore < E > , Cold : ItemStore < E > > {
35
36
db : Arc < HotColdDB < E , Hot , Cold > > ,
36
- #[ allow( clippy:: type_complexity) ]
37
- tx_thread : Option < Mutex < ( mpsc:: Sender < Notification > , thread:: JoinHandle < ( ) > ) > > ,
38
37
/// Record of when the last migration ran, for enforcing `epochs_per_run`.
39
38
prev_migration : Arc < Mutex < PrevMigration > > ,
39
+ tx_thread : Option <
40
+ Mutex < (
41
+ crossbeam_channel:: Sender < Notification > ,
42
+ thread:: JoinHandle < ( ) > ,
43
+ ) > ,
44
+ > ,
40
45
/// Genesis block root, for persisting the `PersistedBeaconChain`.
41
46
genesis_block_root : Hash256 ,
42
47
log : Logger ,
@@ -112,11 +117,13 @@ pub enum PruningError {
112
117
}
113
118
114
119
/// Message sent to the migration thread containing the information it needs to run.
120
+ #[ derive( Debug ) ]
115
121
pub enum Notification {
116
122
Finalization ( FinalizationNotification ) ,
117
123
Reconstruction ,
118
124
}
119
125
126
+ #[ derive( Clone , Debug ) ]
120
127
pub struct FinalizationNotification {
121
128
finalized_state_root : BeaconStateHash ,
122
129
finalized_checkpoint : Checkpoint ,
@@ -203,7 +210,7 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> BackgroundMigrator<E, Ho
203
210
}
204
211
205
212
pub fn run_reconstruction ( db : Arc < HotColdDB < E , Hot , Cold > > , log : & Logger ) {
206
- if let Err ( e) = db. reconstruct_historic_states ( ) {
213
+ if let Err ( e) = db. reconstruct_historic_states ( None ) {
207
214
error ! (
208
215
log,
209
216
"State reconstruction failed" ;
@@ -359,39 +366,83 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> BackgroundMigrator<E, Ho
359
366
db : Arc < HotColdDB < E , Hot , Cold > > ,
360
367
prev_migration : Arc < Mutex < PrevMigration > > ,
361
368
log : Logger ,
362
- ) -> ( mpsc:: Sender < Notification > , thread:: JoinHandle < ( ) > ) {
363
- let ( tx, rx) = mpsc:: channel ( ) ;
369
+ ) -> (
370
+ crossbeam_channel:: Sender < Notification > ,
371
+ thread:: JoinHandle < ( ) > ,
372
+ ) {
373
+ let ( tx, rx) = crossbeam_channel:: unbounded ( ) ;
374
+ let tx_thread = tx. clone ( ) ;
364
375
let thread = thread:: spawn ( move || {
365
- while let Ok ( notif) = rx. recv ( ) {
366
- // Read the rest of the messages in the channel, preferring any reconstruction
367
- // notification, or the finalization notification with the greatest finalized epoch.
368
- let notif =
369
- rx. try_iter ( )
370
- . fold ( notif, |best, other : Notification | match ( & best, & other) {
371
- ( Notification :: Reconstruction , _)
372
- | ( _, Notification :: Reconstruction ) => Notification :: Reconstruction ,
373
- (
374
- Notification :: Finalization ( fin1) ,
375
- Notification :: Finalization ( fin2) ,
376
- ) => {
377
- if fin2. finalized_checkpoint . epoch > fin1. finalized_checkpoint . epoch
378
- {
379
- other
380
- } else {
381
- best
382
- }
383
- }
384
- } ) ;
376
+ let mut sel = crossbeam_channel:: Select :: new ( ) ;
377
+ sel. recv ( & rx) ;
378
+
379
+ loop {
380
+ // Block until sth is in queue
381
+ let _queue_size = sel. ready ( ) ;
382
+ let queue: Vec < Notification > = rx. try_iter ( ) . collect ( ) ;
383
+ debug ! (
384
+ log,
385
+ "New worker thread poll" ;
386
+ "queue" => ?queue
387
+ ) ;
388
+
389
+ // Find a reconstruction notification and best finalization notification.
390
+ let reconstruction_notif = queue
391
+ . iter ( )
392
+ . find ( |n| matches ! ( n, Notification :: Reconstruction ) ) ;
393
+ let migrate_notif = queue
394
+ . iter ( )
395
+ . filter_map ( |n| match n {
396
+ // should not be present anymore
397
+ Notification :: Reconstruction => None ,
398
+ Notification :: Finalization ( f) => Some ( f) ,
399
+ } )
400
+ . max_by_key ( |f| f. finalized_checkpoint . epoch ) ;
401
+
402
+ // Do a bit of state reconstruction first if required.
403
+ if let Some ( _) = reconstruction_notif {
404
+ let timer = std:: time:: Instant :: now ( ) ;
405
+
406
+ match db. reconstruct_historic_states ( Some ( BLOCKS_PER_RECONSTRUCTION ) ) {
407
+ Err ( Error :: StateReconstructionDidNotComplete ) => {
408
+ info ! (
409
+ log,
410
+ "Finished reconstruction batch" ;
411
+ "batch_time_ms" => timer. elapsed( ) . as_millis( )
412
+ ) ;
413
+ // Handle send error
414
+ let _ = tx_thread. send ( Notification :: Reconstruction ) ;
415
+ }
416
+ Err ( e) => {
417
+ error ! (
418
+ log,
419
+ "State reconstruction failed" ;
420
+ "error" => ?e,
421
+ ) ;
422
+ }
423
+ Ok ( ( ) ) => {
424
+ info ! (
425
+ log,
426
+ "Finished state reconstruction" ;
427
+ "batch_time_ms" => timer. elapsed( ) . as_millis( )
428
+ ) ;
429
+ }
430
+ }
431
+ }
432
+
433
+ // Do the finalization migration.
434
+ if let Some ( notif) = migrate_notif {
435
+ let timer = std:: time:: Instant :: now ( ) ;
385
436
386
- // Do not run too frequently.
387
- if let Some ( epoch) = notif. epoch ( ) {
388
437
let mut prev_migration = prev_migration. lock ( ) ;
389
438
439
+ // Do not run too frequently.
440
+ let epoch = notif. finalized_checkpoint . epoch ;
390
441
if let Some ( prev_epoch) = prev_migration. epoch {
391
442
if epoch < prev_epoch + prev_migration. epochs_per_run {
392
443
debug ! (
393
444
log,
394
- "Database consolidation deferred" ;
445
+ "Finalization migration deferred" ;
395
446
"last_finalized_epoch" => prev_epoch,
396
447
"new_finalized_epoch" => epoch,
397
448
"epochs_per_run" => prev_migration. epochs_per_run,
@@ -404,11 +455,14 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> BackgroundMigrator<E, Ho
404
455
// at which we ran. This value isn't tracked on disk so we will always migrate
405
456
// on the first finalization after startup.
406
457
prev_migration. epoch = Some ( epoch) ;
407
- }
408
458
409
- match notif {
410
- Notification :: Reconstruction => Self :: run_reconstruction ( db. clone ( ) , & log) ,
411
- Notification :: Finalization ( fin) => Self :: run_migration ( db. clone ( ) , fin, & log) ,
459
+ Self :: run_migration ( db. clone ( ) , notif. to_owned ( ) , & log) ;
460
+
461
+ info ! (
462
+ log,
463
+ "Finished finalization migration" ;
464
+ "running_time_ms" => timer. elapsed( ) . as_millis( )
465
+ ) ;
412
466
}
413
467
}
414
468
} ) ;
@@ -534,6 +588,7 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> BackgroundMigrator<E, Ho
534
588
535
589
for maybe_tuple in iter {
536
590
let ( block_root, state_root, slot) = maybe_tuple?;
591
+
537
592
let block_root = SignedBeaconBlockHash :: from ( block_root) ;
538
593
let state_root = BeaconStateHash :: from ( state_root) ;
539
594
0 commit comments