diff --git a/libsql/src/database.rs b/libsql/src/database.rs index 838eeb267f..5f27081f33 100644 --- a/libsql/src/database.rs +++ b/libsql/src/database.rs @@ -675,6 +675,18 @@ impl Database { }; use tokio::sync::Mutex; + let _ = tokio::task::block_in_place(move || { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(); + rt.block_on(async { + // we will ignore if any errors occurred during the bootstrapping the db, + // because the client could be offline when trying to connect. + let _ = db.bootstrap_db().await; + }) + }); + let local = db.connect()?; if *remote_writes { diff --git a/libsql/src/local/database.rs b/libsql/src/local/database.rs index 2278ab1c42..7391870f7a 100644 --- a/libsql/src/local/database.rs +++ b/libsql/src/local/database.rs @@ -465,11 +465,21 @@ impl Database { /// Sync WAL frames to remote. pub async fn sync_offline(&self) -> Result { let mut sync_ctx = self.sync_ctx.as_ref().unwrap().lock().await; + // it is important we call `bootstrap` before we `sync`. Because sync uses a connection + // to the db and during bootstrap we replace the sqlite db file. This can lead to + // inconsistencies and data corruption. crate::sync::bootstrap_db(&mut sync_ctx).await?; let conn = self.connect()?; crate::sync::sync_offline(&mut sync_ctx, &conn).await } + #[cfg(feature = "sync")] + /// Brings the .db file from server, if required. + pub async fn bootstrap_db(&self) -> Result<()> { + let mut sync_ctx = self.sync_ctx.as_ref().unwrap().lock().await; + crate::sync::bootstrap_db(&mut sync_ctx).await + } + pub(crate) fn path(&self) -> &str { &self.db_path } diff --git a/libsql/src/sync.rs b/libsql/src/sync.rs index 4a48c5ce8d..3666e4b556 100644 --- a/libsql/src/sync.rs +++ b/libsql/src/sync.rs @@ -686,6 +686,8 @@ async fn atomic_write>(path: P, data: &[u8]) -> Result<()> { /// bootstrap_db brings the .db file from remote, if required. If the .db file already exists, then /// it does nothing. Calling this function multiple times is safe. +/// However, make sure there are no existing active connections to the db file as this method can +/// replace it pub async fn bootstrap_db(sync_ctx: &mut SyncContext) -> Result<()> { // todo: we are checking with the remote server only during initialisation. ideally, // we need to do this when we notice a large gap in generations, when bootstrapping is cheaper