mirror of
https://gitlab.computer.surgery/matrix/grapevine.git
synced 2025-12-18 08:11:24 +01:00
add option to migrate database in-place without copying
I originally didn't plan to include anything like this, but a few people in #grapevine:computer.surgery pointed out use-cases where it might be desirable: - a server that doesn't have enough room to store two full copies of the db. Transferring a copy over the network to back up is viable. - making a reflinked copy on a CoW filesystem (we could support this in the tool, but don't currently) - a server with some other backup mechanism available like snapshotting the entire filesystem
This commit is contained in:
parent
b93d4f471c
commit
3226d3a9eb
3 changed files with 55 additions and 7 deletions
32
src/cli.rs
32
src/cli.rs
|
|
@ -96,12 +96,36 @@ pub(crate) struct MigrateDbArgs {
|
|||
pub(crate) to: DbMigrationTarget,
|
||||
|
||||
/// Path to read database from.
|
||||
#[clap(long = "in", short)]
|
||||
pub(crate) in_path: PathBuf,
|
||||
#[clap(long = "in", short, required_unless_present("inplace_path"))]
|
||||
pub(crate) in_path: Option<PathBuf>,
|
||||
|
||||
/// Path to write migrated database to.
|
||||
#[clap(long = "out", short)]
|
||||
pub(crate) out_path: PathBuf,
|
||||
#[clap(long = "out", short, required_unless_present("inplace_path"))]
|
||||
pub(crate) out_path: Option<PathBuf>,
|
||||
|
||||
/// Path to modify an existing database in-place, instead of copying before
|
||||
/// migrating.
|
||||
///
|
||||
/// Note that even a successful migration may lose data, because some parts
|
||||
/// of the schema present in the initial database may not exist in the
|
||||
/// target version. Because of this, it's very important to have a
|
||||
/// backup of the initial database when migrating. The preferred way to
|
||||
/// do this is with the --in and --out flags, which ensure that the
|
||||
/// original database path is left unmodified. In some situations, it
|
||||
/// may be possible to take a backup some other way (transferring it
|
||||
/// over the network, for example), but copying the files locally is
|
||||
/// undesirable. In this case, setting the --i-have-tested-my-backups
|
||||
/// flag enables the use of --inplace to modify the database without
|
||||
/// copying to a new location first.
|
||||
#[clap(long = "inplace", conflicts_with_all(["in_path", "out_path"]))]
|
||||
pub(crate) inplace_path: Option<PathBuf>,
|
||||
|
||||
/// Set if you have tested your backups, to enable use of the --inplace
|
||||
/// flag.
|
||||
///
|
||||
/// See the documentation of --inplace for more details.
|
||||
#[clap(long)]
|
||||
pub(crate) i_have_tested_my_backups: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
|
|
|
|||
|
|
@ -34,19 +34,33 @@ pub(crate) async fn run(
|
|||
) -> Result<(), error::MigrateDbCommand> {
|
||||
use error::MigrateDbCommand as Error;
|
||||
|
||||
let db_path = if let Some(path) = args.inplace_path {
|
||||
if !args.i_have_tested_my_backups {
|
||||
return Err(Error::InplaceUnconfirmed);
|
||||
}
|
||||
path
|
||||
} else {
|
||||
let in_path = args
|
||||
.in_path
|
||||
.expect("in_path should be required if inplace_path is unset");
|
||||
let out_path = args
|
||||
.out_path
|
||||
.expect("out_path should be required if inplace_path is unset");
|
||||
copy_dir(&in_path, &out_path).await.map_err(Error::Copy)?;
|
||||
out_path
|
||||
};
|
||||
|
||||
let mut config = config::load(args.config.config.as_ref()).await?;
|
||||
// mutating the config like this is ugly, but difficult to avoid. Currently
|
||||
// the database is very tightly coupled with service code, which reads the
|
||||
// path only from the config.
|
||||
args.out_path
|
||||
db_path
|
||||
.to_str()
|
||||
.ok_or(Error::InvalidUnicodeOutPath)?
|
||||
.clone_into(&mut config.database.path);
|
||||
|
||||
let (_guard, reload_handles) = observability::init(&config)?;
|
||||
|
||||
copy_dir(&args.in_path, &args.out_path).await.map_err(Error::Copy)?;
|
||||
|
||||
let db = Box::leak(Box::new(
|
||||
KeyValueDatabase::load_or_create(&config).map_err(Error::LoadDb)?,
|
||||
));
|
||||
|
|
|
|||
10
src/error.rs
10
src/error.rs
|
|
@ -97,6 +97,16 @@ pub(crate) enum MigrateDbCommand {
|
|||
#[error("output path is not valid unicode")]
|
||||
InvalidUnicodeOutPath,
|
||||
|
||||
#[error(
|
||||
"migrating a database may lose data even if the migration is \
|
||||
successful. Because of this, it is very important to ensure you have \
|
||||
a working backup when using the --inplace flag. If you have a tested \
|
||||
backup, set the --i-have-tested-my-backups flag to enable use of \
|
||||
--inplace. Alternatively, use --from and --to instead of --inplace \
|
||||
to ensure the original database is preserved."
|
||||
)]
|
||||
InplaceUnconfirmed,
|
||||
|
||||
#[error("failed to copy existing database directory")]
|
||||
Copy(#[source] CopyDir),
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue