error on meili database version mismatch

This commit is contained in:
mpostma 2020-06-23 17:03:56 +02:00
parent 308630c094
commit f5a936614a
3 changed files with 89 additions and 27 deletions

View File

@ -3,6 +3,7 @@ use std::fs::File;
use std::path::Path; use std::path::Path;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use std::{fs, thread}; use std::{fs, thread};
use std::io::{Read, Write, ErrorKind};
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use crossbeam_channel::{Receiver, Sender}; use crossbeam_channel::{Receiver, Sender};
@ -161,11 +162,62 @@ fn update_awaiter(
Ok(()) Ok(())
} }
/// Ensures Meilisearch version is compatible with the database, returns an error versions mismatch.
/// If create is set to true, a VERSION file is created with the current version.
fn version_guard(path: &Path, create: bool) -> MResult<()> {
let current_version_major = env!("CARGO_PKG_VERSION_MAJOR");
let current_version_minor = env!("CARGO_PKG_VERSION_MINOR");
let current_version_patch = env!("CARGO_PKG_VERSION_PATCH");
let version_path = path.join("VERSION");
match File::open(&version_path) {
Ok(mut file) => {
let mut version = String::new();
file.read_to_string(&mut version)?;
let mut version = version.split(".");
let version_major = version.next().ok_or(Error::VersionMismatch("bad VERSION file".to_string()))?;
let version_minor = version.next().ok_or(Error::VersionMismatch("bad VERSION file".to_string()))?;
if version_major != current_version_major || version_minor != current_version_minor {
return Err(Error::VersionMismatch(format!("{}.{}.XX", version_major, version_major)));
}
}
Err(error) => {
match error.kind() {
ErrorKind::NotFound => {
if create {
// when no version file is found, and we've beem told to create one,
// create a new file wioth the current version in it.
let mut version_file = File::create(&version_path)?;
version_file.write_all(format!("{}.{}.{}",
current_version_major,
current_version_minor,
current_version_patch).as_bytes())?;
} else {
// when no version file is found and we were not told to create one, this
// means that the version is inferior to the one this feature was added.
return Err(Error::VersionMismatch(format!("<0.12.0")));
}
}
_ => return Err(error.into())
}
}
}
Ok(())
}
impl Database { impl Database {
pub fn open_or_create(path: impl AsRef<Path>, options: DatabaseOptions) -> MResult<Database> { pub fn open_or_create(path: impl AsRef<Path>, options: DatabaseOptions) -> MResult<Database> {
let main_path = path.as_ref().join("main"); let main_path = path.as_ref().join("main");
let update_path = path.as_ref().join("update"); let update_path = path.as_ref().join("update");
//create db directory
fs::create_dir_all(&path)?;
// create file only if main db wasn't created before (first run)
version_guard(path.as_ref(), !main_path.exists() && !update_path.exists())?;
fs::create_dir_all(&main_path)?; fs::create_dir_all(&main_path)?;
let env = heed::EnvOpenOptions::new() let env = heed::EnvOpenOptions::new()
.map_size(options.main_map_size) .map_size(options.main_map_size)

View File

@ -15,22 +15,23 @@ pub type MResult<T> = Result<T, Error>;
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
Io(io::Error),
IndexAlreadyExists,
MissingPrimaryKey,
SchemaMissing,
WordIndexMissing,
MissingDocumentId,
MaxFieldsLimitExceeded,
Schema(meilisearch_schema::Error),
Heed(heed::Error),
Fst(fst::Error),
SerdeJson(SerdeJsonError),
Bincode(bincode::Error), Bincode(bincode::Error),
Serializer(SerializerError),
Deserializer(DeserializerError), Deserializer(DeserializerError),
FilterParseError(PestError<Rule>),
FacetError(FacetError), FacetError(FacetError),
FilterParseError(PestError<Rule>),
Fst(fst::Error),
Heed(heed::Error),
IndexAlreadyExists,
Io(io::Error),
MaxFieldsLimitExceeded,
MissingDocumentId,
MissingPrimaryKey,
Schema(meilisearch_schema::Error),
SchemaMissing,
SerdeJson(SerdeJsonError),
Serializer(SerializerError),
VersionMismatch(String),
WordIndexMissing,
} }
impl ErrorCode for Error { impl ErrorCode for Error {
@ -53,6 +54,7 @@ impl ErrorCode for Error {
| Bincode(_) | Bincode(_)
| Serializer(_) | Serializer(_)
| Deserializer(_) | Deserializer(_)
| VersionMismatch(_)
| Io(_) => Code::Internal, | Io(_) => Code::Internal,
} }
} }
@ -141,22 +143,23 @@ impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*; use self::Error::*;
match self { match self {
Io(e) => write!(f, "{}", e),
IndexAlreadyExists => write!(f, "index already exists"),
MissingPrimaryKey => write!(f, "schema cannot be built without a primary key"),
SchemaMissing => write!(f, "this index does not have a schema"),
WordIndexMissing => write!(f, "this index does not have a word index"),
MissingDocumentId => write!(f, "document id is missing"),
MaxFieldsLimitExceeded => write!(f, "maximum number of fields in a document exceeded"),
Schema(e) => write!(f, "schema error; {}", e),
Heed(e) => write!(f, "heed error; {}", e),
Fst(e) => write!(f, "fst error; {}", e),
SerdeJson(e) => write!(f, "serde json error; {}", e),
Bincode(e) => write!(f, "bincode error; {}", e), Bincode(e) => write!(f, "bincode error; {}", e),
Serializer(e) => write!(f, "serializer error; {}", e),
Deserializer(e) => write!(f, "deserializer error; {}", e), Deserializer(e) => write!(f, "deserializer error; {}", e),
FilterParseError(e) => write!(f, "error parsing filter; {}", e),
FacetError(e) => write!(f, "error processing facet filter: {}", e), FacetError(e) => write!(f, "error processing facet filter: {}", e),
FilterParseError(e) => write!(f, "error parsing filter; {}", e),
Fst(e) => write!(f, "fst error; {}", e),
Heed(e) => write!(f, "heed error; {}", e),
IndexAlreadyExists => write!(f, "index already exists"),
Io(e) => write!(f, "{}", e),
MaxFieldsLimitExceeded => write!(f, "maximum number of fields in a document exceeded"),
MissingDocumentId => write!(f, "document id is missing"),
MissingPrimaryKey => write!(f, "schema cannot be built without a primary key"),
Schema(e) => write!(f, "schema error; {}", e),
SchemaMissing => write!(f, "this index does not have a schema"),
SerdeJson(e) => write!(f, "serde json error; {}", e),
Serializer(e) => write!(f, "serializer error; {}", e),
VersionMismatch(version) => write!(f, "Cannot open database, expected Meilisearch version: {}", version),
WordIndexMissing => write!(f, "this index does not have a word index"),
} }
} }
} }

View File

@ -4,6 +4,7 @@ use std::sync::Arc;
use meilisearch_core::{Database, DatabaseOptions}; use meilisearch_core::{Database, DatabaseOptions};
use sha2::Digest; use sha2::Digest;
use sysinfo::Pid; use sysinfo::Pid;
use log::error;
use crate::index_update_callback; use crate::index_update_callback;
use crate::option::Opt; use crate::option::Opt;
@ -66,7 +67,13 @@ impl Data {
let http_payload_size_limit = opt.http_payload_size_limit; let http_payload_size_limit = opt.http_payload_size_limit;
let db = Arc::new(Database::open_or_create(opt.db_path, db_opt).unwrap()); let db = match Database::open_or_create(opt.db_path, db_opt) {
Ok(db) => Arc::new(db),
Err(e) => {
error!("{}", e);
std::process::exit(1);
}
};
let mut api_keys = ApiKeys { let mut api_keys = ApiKeys {
master: opt.master_key, master: opt.master_key,