Skip to content

Commit e5faaf7

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 metadata file exists, but the db file does not exist, 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 e5faaf7

File tree

1 file changed

+54
-10
lines changed

1 file changed

+54
-10
lines changed

libsql/src/sync.rs

Lines changed: 54 additions & 10 deletions
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,63 @@ 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+
// neither the db file nor the metadata file exists, lets bootstrap from remote
538+
tracing::debug!(
539+
"syncing db file from remote server, generation={}",
540+
generation
541+
);
542+
self.sync_db(generation).await
543+
}
544+
(false, true) => {
545+
// kinda inconsistent state: DB exists but metadata missing
546+
// however, this generally not an issue. For a fresh db, a user might do writes
547+
// locally and then try to do sync later. So in this case, we will not
548+
// bootstrap the db file and let the user proceed. If it is not a fresh db, the
549+
// push will fail anyways later.
550+
// if metadata file does not exist, then generation should be zero
551+
assert_eq!(self.durable_generation, 0);
552+
// lets initialise it to first generation
553+
self.durable_generation = 1;
554+
Ok(())
555+
}
556+
(true, false) => {
557+
// inconsistent state: Metadata exists but DB missing
558+
tracing::error!(
559+
"local state is incorrect, metadata file exists but db file does not"
560+
);
561+
Err(SyncError::InvalidLocalState(
562+
"metadata file exists but db file does not".to_string(),
563+
)
564+
.into())
565+
}
566+
(true, true) => {
567+
// both files exists, no need to sync
568+
Ok(())
569+
}
570+
}
533571
}
534572

535573
/// sync_db will download the db file from the remote server and replace the local file.
@@ -831,3 +869,9 @@ async fn try_pull(
831869
})
832870
}
833871
}
872+
873+
fn check_if_file_exists(path: &str) -> core::result::Result<bool, SyncError> {
874+
Path::new(&path)
875+
.try_exists()
876+
.map_err(SyncError::io("metadata file exists"))
877+
}

0 commit comments

Comments
 (0)