@@ -64,6 +64,8 @@ pub enum SyncError {
6464 PullDb ( StatusCode , String ) ,
6565 #[ error( "server returned a lower generation than local: local={0}, remote={1}" ) ]
6666 InvalidLocalGeneration ( u32 , u32 ) ,
67+ #[ error( "invalid local state: {0}" ) ]
68+ InvalidLocalState ( String ) ,
6769}
6870
6971impl SyncError {
@@ -509,27 +511,60 @@ impl SyncContext {
509511 }
510512
511513 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- }
516514 // somehow we are ahead of the remote in generations. following should not happen because
517515 // we checkpoint only if the remote server tells us to do so.
518516 if self . durable_generation > generation {
519517 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 ={}" ,
521519 self . durable_generation,
522520 generation
523521 ) ;
524522 return Err (
525523 SyncError :: InvalidLocalGeneration ( self . durable_generation , generation) . into ( ) ,
526524 ) ;
527525 }
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+ }
533568 }
534569
535570 /// sync_db will download the db file from the remote server and replace the local file.
@@ -831,3 +866,9 @@ async fn try_pull(
831866 } )
832867 }
833868}
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