Skip to content

Commit ddfbd07

Browse files
committed
Change the sync heuristics
we use the following heuristic to determine if we need to sync the db file 1. if no db file or the metadata file exists, then user is starting from scratch and we will do the sync 2. if the db file exists, but the metadata file does not exist (or other way around), then local db is in an incorrect state. we stop and return with an error 3. if the db file exists and the metadata file exists, then we don't need to do the sync
1 parent c708528 commit ddfbd07

File tree

1 file changed

+52
-11
lines changed

1 file changed

+52
-11
lines changed

libsql/src/sync.rs

+52-11
Original file line numberDiff line numberDiff line change
@@ -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

6971
impl 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.
518-
if self.durable_generation > generation {
516+
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

Comments
 (0)