@@ -64,6 +64,8 @@ pub enum SyncError {
64
64
PullDb ( StatusCode , String ) ,
65
65
#[ error( "server returned a lower generation than local: local={0}, remote={1}" ) ]
66
66
InvalidLocalGeneration ( u32 , u32 ) ,
67
+ #[ error( "invalid local state: {0}" ) ]
68
+ InvalidLocalState ( String ) ,
67
69
}
68
70
69
71
impl SyncError {
@@ -509,27 +511,60 @@ impl SyncContext {
509
511
}
510
512
511
513
async fn sync_db_if_needed ( & mut self , generation : u32 ) -> Result < ( ) > {
512
- // we will get the export file only if the remote generation is different from the one we have
513
- if generation == self . durable_generation {
514
- return Ok ( ( ) ) ;
515
- }
516
514
// somehow we are ahead of the remote in generations. following should not happen because
517
515
// we checkpoint only if the remote server tells us to do so.
518
- if self . durable_generation > generation {
516
+ if self . durable_generation >= generation {
519
517
tracing:: error!(
520
- "server returned a lower generation than what we have: sent ={}, got ={}" ,
518
+ "server returned a lower generation than what we have: local ={}, remote ={}" ,
521
519
self . durable_generation,
522
520
generation
523
521
) ;
524
522
return Err (
525
523
SyncError :: InvalidLocalGeneration ( self . durable_generation , generation) . into ( ) ,
526
524
) ;
527
525
}
528
- tracing:: debug!(
529
- "syncing db file from remote server, generation={}" ,
530
- generation
531
- ) ;
532
- self . sync_db ( generation) . await
526
+ // we use the following heuristic to determine if we need to sync the db file
527
+ // 1. if no db file or the metadata file exists, then user is starting from scratch
528
+ // and we will do the sync
529
+ // 2. if the db file exists, but the metadata file does not exist (or other way around),
530
+ // then local db is in an incorrect state. we stop and return with an error
531
+ // 3. if the db file exists and the metadata file exists, then we don't need to do the
532
+ // sync
533
+ let metadata_exists = check_if_file_exists ( & format ! ( "{}-info" , self . db_path) ) ?;
534
+ let db_file_exists = check_if_file_exists ( & self . db_path ) ?;
535
+ match ( metadata_exists, db_file_exists) {
536
+ ( false , false ) => {
537
+ tracing:: debug!(
538
+ "syncing db file from remote server, generation={}" ,
539
+ generation
540
+ ) ;
541
+ self . sync_db ( generation) . await
542
+ }
543
+ ( false , true ) => {
544
+ // inconsistent state: DB exists but metadata missing
545
+ tracing:: error!(
546
+ "local state is incorrect, db file exists but metadata file does not"
547
+ ) ;
548
+ Err ( SyncError :: InvalidLocalState (
549
+ "db file exists but metadata file does not" . to_string ( ) ,
550
+ )
551
+ . into ( ) )
552
+ }
553
+ ( true , false ) => {
554
+ // inconsistent state: Metadata exists but DB missing
555
+ tracing:: error!(
556
+ "local state is incorrect, metadata file exists but db file does not"
557
+ ) ;
558
+ Err ( SyncError :: InvalidLocalState (
559
+ "metadata file exists but db file does not" . to_string ( ) ,
560
+ )
561
+ . into ( ) )
562
+ }
563
+ ( true , true ) => {
564
+ // both files exists, need to sync
565
+ Ok ( ( ) )
566
+ }
567
+ }
533
568
}
534
569
535
570
/// sync_db will download the db file from the remote server and replace the local file.
@@ -831,3 +866,9 @@ async fn try_pull(
831
866
} )
832
867
}
833
868
}
869
+
870
+ fn check_if_file_exists ( path : & str ) -> core:: result:: Result < bool , SyncError > {
871
+ Path :: new ( & path)
872
+ . try_exists ( )
873
+ . map_err ( SyncError :: io ( "metadata file exists" ) )
874
+ }
0 commit comments