Skip to content

Commit 4cf09d8

Browse files
committed
more robust dbs folder swap
1 parent a7c4533 commit 4cf09d8

2 files changed

Lines changed: 29 additions & 5 deletions

File tree

libsql-server/src/bottomless_migrate.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,26 @@ use crate::namespace::meta_store::{MetaStore, MetaStoreHandle};
2626
use crate::namespace::NamespaceStore;
2727

2828
/// The process for migrating from bottomless to libsql wal is simple:
29-
/// 1) iteratate over all namespaces, and make sure that they
29+
/// 1) iteratate over all namespaces, and make sure that they are up to date with bottomless by
30+
/// loading them
31+
/// 2) with a dummy registry, in a temp directory, with no storage, and no checkpointer, inject all the pages from the
32+
/// original db into a new temp db
33+
/// 3) when all namspace have been successfully migrated, make the dbs and wals folders permanent
3034
pub async fn bottomless_migrate(
3135
meta_store: MetaStore,
3236
base_config: BaseNamespaceConfig,
3337
primary_config: PrimaryConfig,
3438
) -> anyhow::Result<()>
3539
{
40+
let base_dbs_dir = base_config.base_path.join("dbs");
41+
let base_dbs_dir_tmp = base_config.base_path.join("_dbs");
42+
// the previous migration failed. The _dbs is still present, but the wals is not. In this case
43+
// we delete the current dbs if it exists and replace it with _dbs, and attempt migration again
44+
if base_dbs_dir_tmp.try_exists()? {
45+
tokio::fs::remove_dir_all(&base_dbs_dir).await?;
46+
tokio::fs::rename(&base_dbs_dir_tmp, &base_dbs_dir).await?;
47+
}
48+
3649
tracing::info!("attempting bottomless migration to libsql-wal");
3750

3851
let tmp = TempDir::new()?;
@@ -90,11 +103,14 @@ pub async fn bottomless_migrate(
90103

91104
tmp_registry.shutdown().await?;
92105

93-
// FIXME: this is not atomic!!
94-
tokio::fs::remove_dir_all(base_config.base_path.join("dbs")).await?;
95-
tokio::fs::rename(tmp.path().join("dbs"), base_config.base_path.join("dbs")).await?;
106+
// unix prevents atomically renaming directories with mv, so we first rename dbs to _dbs, then
107+
// move the new dbs and wals, then remove old dbs.
108+
// when we perform a check form migration, whe verify if _dbs exists. If it exists, and wals
109+
// doesn't exist, then we restore it, otherwise, we delete it.
110+
tokio::fs::rename(&base_dbs_dir, &base_dbs_dir_tmp).await?;
111+
tokio::fs::rename(tmp.path().join("dbs"), base_dbs_dir).await?;
96112
tokio::fs::rename(tmp.path().join("wals"), base_config.base_path.join("wals")).await?;
97-
113+
tokio::fs::remove_dir_all(base_config.base_path.join("_dbs")).await?;
98114

99115
Ok(())
100116
}

libsql-server/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,6 +1179,14 @@ where
11791179
bottomless_migrate(meta_store, base_config.clone(), primary_config.clone()).await?;
11801180
Ok(true)
11811181
} else {
1182+
// the wals directory is present and so is the _dbs. This means that a crash occured
1183+
// before we could remove it. clean it up now. see code in `migrate_bottomless.rs`
1184+
let tmp_dbs_path = base_config.base_path.join("_dbs");
1185+
if tmp_dbs_path.try_exists()? {
1186+
tracing::info!("removed dangling `_dbs` folder");
1187+
tokio::fs::remove_dir_all(&tmp_dbs_path).await?;
1188+
}
1189+
11821190
tracing::info!("bottomless already migrated, skipping...");
11831191
Ok(false)
11841192
}

0 commit comments

Comments
 (0)