mirror of
https://github.com/meilisearch/MeiliSearch
synced 2025-07-04 20:37:15 +02:00
test snapshots
This commit is contained in:
parent
0448f0ce56
commit
85ae34cf9f
8 changed files with 579 additions and 138 deletions
|
@ -1,5 +1,5 @@
|
|||
pub mod error;
|
||||
mod index_store;
|
||||
pub mod index_store;
|
||||
pub mod uuid_store;
|
||||
|
||||
use std::path::Path;
|
||||
|
|
|
@ -11,20 +11,26 @@ use tokio::time::sleep;
|
|||
use crate::compression::from_tar_gz;
|
||||
use crate::index_controller::updates::UpdateMsg;
|
||||
|
||||
use super::index_resolver::HardStateIndexResolver;
|
||||
use super::index_resolver::IndexResolver;
|
||||
use super::index_resolver::index_store::IndexStore;
|
||||
use super::index_resolver::uuid_store::UuidStore;
|
||||
use super::updates::UpdateSender;
|
||||
|
||||
pub struct SnapshotService {
|
||||
index_resolver: Arc<HardStateIndexResolver>,
|
||||
pub struct SnapshotService<U, I> {
|
||||
index_resolver: Arc<IndexResolver<U, I>>,
|
||||
update_sender: UpdateSender,
|
||||
snapshot_period: Duration,
|
||||
snapshot_path: PathBuf,
|
||||
db_name: String,
|
||||
}
|
||||
|
||||
impl SnapshotService {
|
||||
impl<U, I> SnapshotService<U, I>
|
||||
where
|
||||
U: UuidStore + Sync + Send + 'static,
|
||||
I: IndexStore + Sync + Send + 'static,
|
||||
{
|
||||
pub fn new(
|
||||
index_resolver: Arc<HardStateIndexResolver>,
|
||||
index_resolver: Arc<IndexResolver<U, I>>,
|
||||
update_sender: UpdateSender,
|
||||
snapshot_period: Duration,
|
||||
snapshot_path: PathBuf,
|
||||
|
@ -127,131 +133,161 @@ pub fn load_snapshot(
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
//use std::iter::FromIterator;
|
||||
//use std::{collections::HashSet, sync::Arc};
|
||||
use std::{collections::HashSet, sync::Arc};
|
||||
|
||||
//use futures::future::{err, ok};
|
||||
//use rand::Rng;
|
||||
//use tokio::time::timeout;
|
||||
//use uuid::Uuid;
|
||||
use futures::future::{err, ok};
|
||||
use once_cell::sync::Lazy;
|
||||
use rand::Rng;
|
||||
use uuid::Uuid;
|
||||
|
||||
//use super::*;
|
||||
use crate::index::error::IndexError;
|
||||
use crate::index::test::Mocker;
|
||||
use crate::index::{Index, error::Result as IndexResult};
|
||||
use crate::index_controller::index_resolver::IndexResolver;
|
||||
use crate::index_controller::index_resolver::error::IndexResolverError;
|
||||
use crate::index_controller::index_resolver::uuid_store::MockUuidStore;
|
||||
use crate::index_controller::index_resolver::index_store::MockIndexStore;
|
||||
use crate::index_controller::updates::create_update_handler;
|
||||
|
||||
//#[actix_rt::test]
|
||||
//async fn test_normal() {
|
||||
//let mut rng = rand::thread_rng();
|
||||
//let uuids_num: usize = rng.gen_range(5..10);
|
||||
//let uuids = (0..uuids_num)
|
||||
//.map(|_| Uuid::new_v4())
|
||||
//.collect::<HashSet<_>>();
|
||||
use super::*;
|
||||
|
||||
//let mut uuid_resolver = MockUuidResolverHandle::new();
|
||||
//let uuids_clone = uuids.clone();
|
||||
//uuid_resolver
|
||||
//.expect_snapshot()
|
||||
//.times(1)
|
||||
//.returning(move |_| Box::pin(ok(uuids_clone.clone())));
|
||||
fn setup() {
|
||||
static SETUP: Lazy<()> = Lazy::new(|| {
|
||||
if cfg!(windows) {
|
||||
std::env::set_var("TMP", ".");
|
||||
} else {
|
||||
std::env::set_var("TMPDIR", ".");
|
||||
}
|
||||
});
|
||||
|
||||
//let uuids_clone = uuids.clone();
|
||||
//let mut index_handle = MockIndexActorHandle::new();
|
||||
//index_handle
|
||||
//.expect_snapshot()
|
||||
//.withf(move |uuid, _path| uuids_clone.contains(uuid))
|
||||
//.times(uuids_num)
|
||||
//.returning(move |_, _| Box::pin(ok(())));
|
||||
// just deref to make sure the env is setup
|
||||
*SETUP
|
||||
}
|
||||
|
||||
//let dir = tempfile::tempdir_in(".").unwrap();
|
||||
//let handle = Arc::new(index_handle);
|
||||
//let update_handle =
|
||||
//UpdateActorHandleImpl::<Vec<u8>>::new(handle.clone(), dir.path(), 4096 * 100).unwrap();
|
||||
#[actix_rt::test]
|
||||
async fn test_normal() {
|
||||
setup();
|
||||
|
||||
//let snapshot_path = tempfile::tempdir_in(".").unwrap();
|
||||
//let snapshot_service = SnapshotService::new(
|
||||
//uuid_resolver,
|
||||
//update_handle,
|
||||
//Duration::from_millis(100),
|
||||
//snapshot_path.path().to_owned(),
|
||||
//"data.ms".to_string(),
|
||||
//);
|
||||
let mut rng = rand::thread_rng();
|
||||
let uuids_num: usize = rng.gen_range(5..10);
|
||||
let uuids = (0..uuids_num)
|
||||
.map(|_| Uuid::new_v4())
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
//snapshot_service.perform_snapshot().await.unwrap();
|
||||
//}
|
||||
let mut uuid_store = MockUuidStore::new();
|
||||
let uuids_clone = uuids.clone();
|
||||
uuid_store
|
||||
.expect_snapshot()
|
||||
.times(1)
|
||||
.returning(move |_| Box::pin(ok(uuids_clone.clone())));
|
||||
|
||||
//#[actix_rt::test]
|
||||
//async fn error_performing_uuid_snapshot() {
|
||||
//let mut uuid_resolver = MockUuidResolverHandle::new();
|
||||
//uuid_resolver
|
||||
//.expect_snapshot()
|
||||
//.times(1)
|
||||
////abitrary error
|
||||
//.returning(|_| Box::pin(err(UuidResolverError::NameAlreadyExist)));
|
||||
let mut indexes = uuids.clone().into_iter().map(|uuid| {
|
||||
let mocker = Mocker::default();
|
||||
mocker.when("snapshot").times(1).then(|_: &Path| -> IndexResult<()> { Ok(()) });
|
||||
mocker.when("uuid").then(move |_: ()| uuid);
|
||||
Index::faux(mocker)
|
||||
});
|
||||
|
||||
//let update_handle = MockUpdateActorHandle::new();
|
||||
let uuids_clone = uuids.clone();
|
||||
let mut index_store = MockIndexStore::new();
|
||||
index_store
|
||||
.expect_get()
|
||||
.withf(move |uuid| uuids_clone.contains(uuid))
|
||||
.times(uuids_num)
|
||||
.returning(move |_| Box::pin(ok(Some(indexes.next().unwrap()))));
|
||||
|
||||
//let snapshot_path = tempfile::tempdir_in(".").unwrap();
|
||||
//let snapshot_service = SnapshotService::new(
|
||||
//uuid_resolver,
|
||||
//update_handle,
|
||||
//Duration::from_millis(100),
|
||||
//snapshot_path.path().to_owned(),
|
||||
//"data.ms".to_string(),
|
||||
//);
|
||||
let index_resolver = Arc::new(IndexResolver::new(uuid_store, index_store));
|
||||
|
||||
//assert!(snapshot_service.perform_snapshot().await.is_err());
|
||||
////Nothing was written to the file
|
||||
//assert!(!snapshot_path.path().join("data.ms.snapshot").exists());
|
||||
//}
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let update_sender = create_update_handler(index_resolver.clone(), dir.path(), 4096 * 100).unwrap();
|
||||
|
||||
//#[actix_rt::test]
|
||||
//async fn error_performing_index_snapshot() {
|
||||
//let uuid = Uuid::new_v4();
|
||||
//let mut uuid_resolver = MockUuidResolverHandle::new();
|
||||
//uuid_resolver
|
||||
//.expect_snapshot()
|
||||
//.times(1)
|
||||
//.returning(move |_| Box::pin(ok(HashSet::from_iter(Some(uuid)))));
|
||||
let snapshot_path = tempfile::tempdir().unwrap();
|
||||
let snapshot_service = SnapshotService::new(
|
||||
index_resolver,
|
||||
update_sender,
|
||||
Duration::from_millis(100),
|
||||
snapshot_path.path().to_owned(),
|
||||
"data.ms".to_string(),
|
||||
);
|
||||
|
||||
//let mut update_handle = MockUpdateActorHandle::new();
|
||||
//update_handle
|
||||
//.expect_snapshot()
|
||||
////abitrary error
|
||||
//.returning(|_, _| Box::pin(err(UpdateActorError::UnexistingUpdate(0))));
|
||||
snapshot_service.perform_snapshot().await.unwrap();
|
||||
}
|
||||
|
||||
//let snapshot_path = tempfile::tempdir_in(".").unwrap();
|
||||
//let snapshot_service = SnapshotService::new(
|
||||
//uuid_resolver,
|
||||
//update_handle,
|
||||
//Duration::from_millis(100),
|
||||
//snapshot_path.path().to_owned(),
|
||||
//"data.ms".to_string(),
|
||||
//);
|
||||
#[actix_rt::test]
|
||||
async fn error_performing_uuid_snapshot() {
|
||||
setup();
|
||||
|
||||
//assert!(snapshot_service.perform_snapshot().await.is_err());
|
||||
////Nothing was written to the file
|
||||
//assert!(!snapshot_path.path().join("data.ms.snapshot").exists());
|
||||
//}
|
||||
let mut uuid_store = MockUuidStore::new();
|
||||
uuid_store
|
||||
.expect_snapshot()
|
||||
.once()
|
||||
.returning(move |_| Box::pin(err(IndexResolverError::IndexAlreadyExists)));
|
||||
|
||||
//#[actix_rt::test]
|
||||
//async fn test_loop() {
|
||||
//let mut uuid_resolver = MockUuidResolverHandle::new();
|
||||
//uuid_resolver
|
||||
//.expect_snapshot()
|
||||
////we expect the funtion to be called between 2 and 3 time in the given interval.
|
||||
//.times(2..4)
|
||||
////abitrary error, to short-circuit the function
|
||||
//.returning(move |_| Box::pin(err(UuidResolverError::NameAlreadyExist)));
|
||||
let mut index_store = MockIndexStore::new();
|
||||
index_store
|
||||
.expect_get()
|
||||
.never();
|
||||
|
||||
//let update_handle = MockUpdateActorHandle::new();
|
||||
let index_resolver = Arc::new(IndexResolver::new(uuid_store, index_store));
|
||||
|
||||
//let snapshot_path = tempfile::tempdir_in(".").unwrap();
|
||||
//let snapshot_service = SnapshotService::new(
|
||||
//uuid_resolver,
|
||||
//update_handle,
|
||||
//Duration::from_millis(100),
|
||||
//snapshot_path.path().to_owned(),
|
||||
//"data.ms".to_string(),
|
||||
//);
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let update_sender = create_update_handler(index_resolver.clone(), dir.path(), 4096 * 100).unwrap();
|
||||
|
||||
//let _ = timeout(Duration::from_millis(300), snapshot_service.run()).await;
|
||||
//}
|
||||
let snapshot_path = tempfile::tempdir().unwrap();
|
||||
let snapshot_service = SnapshotService::new(
|
||||
index_resolver,
|
||||
update_sender,
|
||||
Duration::from_millis(100),
|
||||
snapshot_path.path().to_owned(),
|
||||
"data.ms".to_string(),
|
||||
);
|
||||
|
||||
assert!(snapshot_service.perform_snapshot().await.is_err());
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn error_performing_index_snapshot() {
|
||||
setup();
|
||||
|
||||
let uuids: HashSet<Uuid> = vec![Uuid::new_v4()].into_iter().collect();
|
||||
|
||||
let mut uuid_store = MockUuidStore::new();
|
||||
let uuids_clone = uuids.clone();
|
||||
uuid_store
|
||||
.expect_snapshot()
|
||||
.once()
|
||||
.returning(move |_| Box::pin(ok(uuids_clone.clone())));
|
||||
|
||||
let mut indexes = uuids.clone().into_iter().map(|uuid| {
|
||||
let mocker = Mocker::default();
|
||||
// index returns random error
|
||||
mocker.when("snapshot").then(|_: &Path| -> IndexResult<()> { Err(IndexError::ExistingPrimaryKey) });
|
||||
mocker.when("uuid").then(move |_: ()| uuid);
|
||||
Index::faux(mocker)
|
||||
});
|
||||
|
||||
let uuids_clone = uuids.clone();
|
||||
let mut index_store = MockIndexStore::new();
|
||||
index_store
|
||||
.expect_get()
|
||||
.withf(move |uuid| uuids_clone.contains(uuid))
|
||||
.once()
|
||||
.returning(move |_| Box::pin(ok(Some(indexes.next().unwrap()))));
|
||||
|
||||
let index_resolver = Arc::new(IndexResolver::new(uuid_store, index_store));
|
||||
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let update_sender = create_update_handler(index_resolver.clone(), dir.path(), 4096 * 100).unwrap();
|
||||
|
||||
let snapshot_path = tempfile::tempdir().unwrap();
|
||||
let snapshot_service = SnapshotService::new(
|
||||
index_resolver,
|
||||
update_sender,
|
||||
Duration::from_millis(100),
|
||||
snapshot_path.path().to_owned(),
|
||||
"data.ms".to_string(),
|
||||
);
|
||||
|
||||
assert!(snapshot_service.perform_snapshot().await.is_err());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,7 @@ use std::fmt;
|
|||
|
||||
use meilisearch_error::{Code, ErrorCode};
|
||||
|
||||
use crate::{
|
||||
document_formats::DocumentFormatError,
|
||||
index_controller::{update_file_store::UpdateFileStoreError, DocumentAdditionFormat},
|
||||
};
|
||||
use crate::{document_formats::DocumentFormatError, index::error::IndexError, index_controller::{update_file_store::UpdateFileStoreError, DocumentAdditionFormat}};
|
||||
|
||||
pub type Result<T> = std::result::Result<T, UpdateLoopError>;
|
||||
|
||||
|
@ -28,6 +25,8 @@ pub enum UpdateLoopError {
|
|||
PayloadError(#[from] actix_web::error::PayloadError),
|
||||
#[error("A {0} payload is missing.")]
|
||||
MissingPayload(DocumentAdditionFormat),
|
||||
#[error("{0}")]
|
||||
IndexError(#[from] IndexError),
|
||||
}
|
||||
|
||||
impl<T> From<tokio::sync::mpsc::error::SendError<T>> for UpdateLoopError
|
||||
|
@ -58,7 +57,6 @@ impl ErrorCode for UpdateLoopError {
|
|||
match self {
|
||||
Self::UnexistingUpdate(_) => Code::NotFound,
|
||||
Self::Internal(_) => Code::Internal,
|
||||
//Self::IndexActor(e) => e.error_code(),
|
||||
Self::FatalUpdateStoreError => Code::Internal,
|
||||
Self::DocumentFormatError(error) => error.error_code(),
|
||||
Self::PayloadError(error) => match error {
|
||||
|
@ -66,6 +64,7 @@ impl ErrorCode for UpdateLoopError {
|
|||
_ => Code::Internal,
|
||||
},
|
||||
Self::MissingPayload(_) => Code::MissingPayload,
|
||||
Self::IndexError(e) => e.error_code(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,16 +26,22 @@ use crate::index::{Index, Settings, Unchecked};
|
|||
use crate::index_controller::update_file_store::UpdateFileStore;
|
||||
use status::UpdateStatus;
|
||||
|
||||
use super::index_resolver::HardStateIndexResolver;
|
||||
use super::index_resolver::index_store::IndexStore;
|
||||
use super::index_resolver::uuid_store::UuidStore;
|
||||
use super::index_resolver::IndexResolver;
|
||||
use super::{DocumentAdditionFormat, Update};
|
||||
|
||||
pub type UpdateSender = mpsc::Sender<UpdateMsg>;
|
||||
|
||||
pub fn create_update_handler(
|
||||
index_resolver: Arc<HardStateIndexResolver>,
|
||||
pub fn create_update_handler<U, I>(
|
||||
index_resolver: Arc<IndexResolver<U, I>>,
|
||||
db_path: impl AsRef<Path>,
|
||||
update_store_size: usize,
|
||||
) -> anyhow::Result<UpdateSender> {
|
||||
) -> anyhow::Result<UpdateSender>
|
||||
where
|
||||
U: UuidStore + Sync + Send + 'static,
|
||||
I: IndexStore + Sync + Send + 'static,
|
||||
{
|
||||
let path = db_path.as_ref().to_owned();
|
||||
let (sender, receiver) = mpsc::channel(100);
|
||||
let actor = UpdateLoop::new(update_store_size, receiver, path, index_resolver)?;
|
||||
|
@ -95,12 +101,16 @@ pub struct UpdateLoop {
|
|||
}
|
||||
|
||||
impl UpdateLoop {
|
||||
pub fn new(
|
||||
pub fn new<U, I>(
|
||||
update_db_size: usize,
|
||||
inbox: mpsc::Receiver<UpdateMsg>,
|
||||
path: impl AsRef<Path>,
|
||||
index_resolver: Arc<HardStateIndexResolver>,
|
||||
) -> anyhow::Result<Self> {
|
||||
index_resolver: Arc<IndexResolver<U, I>>,
|
||||
) -> anyhow::Result<Self>
|
||||
where
|
||||
U: UuidStore + Sync + Send + 'static,
|
||||
I: IndexStore + Sync + Send + 'static,
|
||||
{
|
||||
let path = path.as_ref().to_owned();
|
||||
std::fs::create_dir_all(&path)?;
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@ use codec::*;
|
|||
use super::error::Result;
|
||||
use super::status::{Enqueued, Processing};
|
||||
use crate::index::Index;
|
||||
use crate::index_controller::index_resolver::index_store::IndexStore;
|
||||
use crate::index_controller::index_resolver::uuid_store::UuidStore;
|
||||
use crate::index_controller::updates::*;
|
||||
use crate::EnvSizer;
|
||||
|
||||
|
@ -157,13 +159,17 @@ impl UpdateStore {
|
|||
))
|
||||
}
|
||||
|
||||
pub fn open(
|
||||
pub fn open<U, I>(
|
||||
options: EnvOpenOptions,
|
||||
path: impl AsRef<Path>,
|
||||
index_resolver: Arc<HardStateIndexResolver>,
|
||||
index_resolver: Arc<IndexResolver<U, I>>,
|
||||
must_exit: Arc<AtomicBool>,
|
||||
update_file_store: UpdateFileStore,
|
||||
) -> anyhow::Result<Arc<Self>> {
|
||||
) -> anyhow::Result<Arc<Self>>
|
||||
where
|
||||
U: UuidStore + Sync + Send + 'static,
|
||||
I: IndexStore + Sync + Send + 'static,
|
||||
{
|
||||
let (update_store, mut notification_receiver) =
|
||||
Self::new(options, path, update_file_store)?;
|
||||
let update_store = Arc::new(update_store);
|
||||
|
@ -296,10 +302,14 @@ impl UpdateStore {
|
|||
/// Executes the user provided function on the next pending update (the one with the lowest id).
|
||||
/// This is asynchronous as it let the user process the update with a read-only txn and
|
||||
/// only writing the result meta to the processed-meta store *after* it has been processed.
|
||||
fn process_pending_update(
|
||||
fn process_pending_update<U, I>(
|
||||
&self,
|
||||
index_resolver: Arc<HardStateIndexResolver>,
|
||||
) -> Result<Option<()>> {
|
||||
index_resolver: Arc<IndexResolver<U, I>>,
|
||||
) -> Result<Option<()>>
|
||||
where
|
||||
U: UuidStore + Sync + Send + 'static,
|
||||
I: IndexStore + Sync + Send + 'static,
|
||||
{
|
||||
// Create a read transaction to be able to retrieve the pending update in order.
|
||||
let rtxn = self.env.read_txn()?;
|
||||
let first_meta = self.pending_queue.first(&rtxn)?;
|
||||
|
@ -325,13 +335,17 @@ impl UpdateStore {
|
|||
}
|
||||
}
|
||||
|
||||
fn perform_update(
|
||||
fn perform_update<U, I>(
|
||||
&self,
|
||||
processing: Processing,
|
||||
index_resolver: Arc<HardStateIndexResolver>,
|
||||
index_resolver: Arc<IndexResolver<U, I>>,
|
||||
index_uuid: Uuid,
|
||||
global_id: u64,
|
||||
) -> Result<Option<()>> {
|
||||
) -> Result<Option<()>>
|
||||
where
|
||||
U: UuidStore + Sync + Send + 'static,
|
||||
I: IndexStore + Sync + Send + 'static,
|
||||
{
|
||||
// Process the pending update using the provided user function.
|
||||
let handle = Handle::current();
|
||||
let update_id = processing.id();
|
||||
|
@ -519,8 +533,7 @@ impl UpdateStore {
|
|||
} = pending.decode()?
|
||||
{
|
||||
self.update_file_store
|
||||
.snapshot(content_uuid, &path)
|
||||
.unwrap();
|
||||
.snapshot(content_uuid, &path)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -528,8 +541,7 @@ impl UpdateStore {
|
|||
let path = path.as_ref().to_owned();
|
||||
indexes
|
||||
.par_iter()
|
||||
.try_for_each(|index| index.snapshot(&path))
|
||||
.unwrap();
|
||||
.try_for_each(|index| index.snapshot(&path))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue