mirror of
https://github.com/meilisearch/MeiliSearch
synced 2025-05-25 09:03:59 +02:00
Support rollback
This commit is contained in:
parent
42fae9994d
commit
a03eef6511
@ -3,8 +3,8 @@ use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
|||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use heed::{types::*, DatabaseStat, WithoutTls};
|
use heed::types::*;
|
||||||
use heed::{CompactionOption, Database, RoTxn, RwTxn, Unspecified};
|
use heed::{CompactionOption, Database, DatabaseStat, RoTxn, RwTxn, Unspecified, WithoutTls};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use roaring::RoaringBitmap;
|
use roaring::RoaringBitmap;
|
||||||
use rstar::RTree;
|
use rstar::RTree;
|
||||||
@ -107,6 +107,7 @@ pub mod db_name {
|
|||||||
pub const VECTOR_ARROY: &str = "vector-arroy";
|
pub const VECTOR_ARROY: &str = "vector-arroy";
|
||||||
pub const DOCUMENTS: &str = "documents";
|
pub const DOCUMENTS: &str = "documents";
|
||||||
}
|
}
|
||||||
|
const NUMBER_OF_DBS: u32 = 25;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Index {
|
pub struct Index {
|
||||||
@ -186,7 +187,7 @@ impl Index {
|
|||||||
) -> Result<Index> {
|
) -> Result<Index> {
|
||||||
use db_name::*;
|
use db_name::*;
|
||||||
|
|
||||||
options.max_dbs(25);
|
options.max_dbs(NUMBER_OF_DBS);
|
||||||
|
|
||||||
let env = unsafe { options.open(path) }?;
|
let env = unsafe { options.open(path) }?;
|
||||||
let mut wtxn = env.write_txn()?;
|
let mut wtxn = env.write_txn()?;
|
||||||
@ -261,11 +262,7 @@ impl Index {
|
|||||||
if this.get_version(&wtxn)?.is_none() && creation {
|
if this.get_version(&wtxn)?.is_none() && creation {
|
||||||
this.put_version(
|
this.put_version(
|
||||||
&mut wtxn,
|
&mut wtxn,
|
||||||
(
|
(constants::VERSION_MAJOR, constants::VERSION_MINOR, constants::VERSION_PATCH),
|
||||||
constants::VERSION_MAJOR,
|
|
||||||
constants::VERSION_MINOR,
|
|
||||||
constants::VERSION_PATCH,
|
|
||||||
),
|
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
wtxn.commit()?;
|
wtxn.commit()?;
|
||||||
@ -284,6 +281,76 @@ impl Index {
|
|||||||
Self::new_with_creation_dates(options, path, now, now, creation)
|
Self::new_with_creation_dates(options, path, now, now, creation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempts to rollback the index at `path` to the version specified by `requested_version`.
|
||||||
|
pub fn rollback<P: AsRef<Path>>(
|
||||||
|
mut options: heed::EnvOpenOptions<WithoutTls>,
|
||||||
|
path: P,
|
||||||
|
requested_version: (u32, u32, u32),
|
||||||
|
) -> Result<RollbackOutcome> {
|
||||||
|
options.max_dbs(NUMBER_OF_DBS);
|
||||||
|
|
||||||
|
// optimistically check if the index is already at the requested version.
|
||||||
|
let env = unsafe { options.open(path.as_ref()) }?;
|
||||||
|
let rtxn = env.read_txn()?;
|
||||||
|
let Some(main) = env.database_options().name(db_name::MAIN).open(&rtxn)? else {
|
||||||
|
return Err(crate::Error::InternalError(crate::InternalError::DatabaseMissingEntry {
|
||||||
|
db_name: db_name::MAIN,
|
||||||
|
key: None,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
let rollback_version =
|
||||||
|
main.remap_types::<Str, VersionCodec>().get(&rtxn, main_key::VERSION_KEY)?;
|
||||||
|
if rollback_version == Some(requested_version) {
|
||||||
|
return Ok(RollbackOutcome::NoRollback);
|
||||||
|
}
|
||||||
|
|
||||||
|
// explicitly drop the environment before reopening it.
|
||||||
|
drop(rtxn);
|
||||||
|
drop(env);
|
||||||
|
|
||||||
|
// really need to rollback then...
|
||||||
|
unsafe { options.flags(heed::EnvFlags::PREV_SNAPSHOT) };
|
||||||
|
let env = unsafe { options.open(path) }?;
|
||||||
|
let mut wtxn = env.write_txn()?;
|
||||||
|
let Some(main) = env.database_options().name(db_name::MAIN).open(&wtxn)? else {
|
||||||
|
return Err(crate::Error::InternalError(crate::InternalError::DatabaseMissingEntry {
|
||||||
|
db_name: db_name::MAIN,
|
||||||
|
key: None,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
let main = main.remap_key_type::<Str>();
|
||||||
|
|
||||||
|
let Some(rollback_version) =
|
||||||
|
main.remap_data_type::<VersionCodec>().get(&wtxn, main_key::VERSION_KEY)?
|
||||||
|
else {
|
||||||
|
return Ok(RollbackOutcome::VersionMismatch {
|
||||||
|
requested_version,
|
||||||
|
rollback_version: None,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if requested_version != rollback_version {
|
||||||
|
return Ok(RollbackOutcome::VersionMismatch {
|
||||||
|
requested_version,
|
||||||
|
rollback_version: Some(rollback_version),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is a bit of a trick to force a change in the index
|
||||||
|
// which is necessary to actually discard the next snapshot, replacing it with this transaction.
|
||||||
|
let now = time::OffsetDateTime::now_utc();
|
||||||
|
main.remap_data_type::<SerdeJson<OffsetDateTime>>().put(
|
||||||
|
&mut wtxn,
|
||||||
|
main_key::UPDATED_AT_KEY,
|
||||||
|
&OffsetDateTime(now),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
wtxn.commit()?;
|
||||||
|
|
||||||
|
Ok(RollbackOutcome::Rollback)
|
||||||
|
}
|
||||||
|
|
||||||
fn set_creation_dates(
|
fn set_creation_dates(
|
||||||
env: &heed::Env<WithoutTls>,
|
env: &heed::Env<WithoutTls>,
|
||||||
main: Database<Unspecified, Unspecified>,
|
main: Database<Unspecified, Unspecified>,
|
||||||
@ -1864,6 +1931,35 @@ pub enum PrefixSearch {
|
|||||||
Disabled,
|
Disabled,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum RollbackOutcome {
|
||||||
|
VersionMismatch {
|
||||||
|
requested_version: (u32, u32, u32),
|
||||||
|
rollback_version: Option<(u32, u32, u32)>,
|
||||||
|
},
|
||||||
|
Rollback,
|
||||||
|
NoRollback,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RollbackOutcome {
|
||||||
|
pub fn succeeded(&self) -> bool {
|
||||||
|
matches!(self, RollbackOutcome::Rollback | RollbackOutcome::NoRollback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for RollbackOutcome {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
RollbackOutcome::VersionMismatch { requested_version, rollback_version: Some(rollback_version) } => write!(f, "cannot rollback to the requested version\n - note: requested version is v{}.{}.{}\n - note: only possible to rollback to v{}.{}.{}",
|
||||||
|
requested_version.0, requested_version.1, requested_version.2, rollback_version.0, rollback_version.1, rollback_version.2),
|
||||||
|
RollbackOutcome::VersionMismatch { requested_version, rollback_version: None } => write!(f, "cannot rollback to the requested version\n - note: requested version is v{}.{}.{}\n - note: only possible to rollback to an unknown version",
|
||||||
|
requested_version.0, requested_version.1, requested_version.2),
|
||||||
|
RollbackOutcome::Rollback => f.write_str("rollback complete"),
|
||||||
|
RollbackOutcome::NoRollback => f.write_str("no rollback necessary"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
struct OffsetDateTime(#[serde(with = "time::serde::rfc3339")] time::OffsetDateTime);
|
struct OffsetDateTime(#[serde(with = "time::serde::rfc3339")] time::OffsetDateTime);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user