mirror of
https://github.com/meilisearch/meilisearch.git
synced 2025-09-12 07:36:29 +00:00
[WIP] rebase on main
This commit is contained in:
@ -13,7 +13,7 @@ use tokio::sync::mpsc;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::{PayloadData, Result, UpdateError, UpdateMsg, UpdateStore, UpdateStoreInfo};
|
||||
use crate::index_controller::index_actor::{IndexActorHandle, CONCURRENT_INDEX_MSG};
|
||||
use crate::index_controller::{index_actor::{IndexActorHandle, CONCURRENT_INDEX_MSG}};
|
||||
use crate::index_controller::{UpdateMeta, UpdateStatus};
|
||||
|
||||
pub struct UpdateActor<D, I> {
|
||||
@ -71,16 +71,14 @@ where
|
||||
Some(Delete { uuid, ret }) => {
|
||||
let _ = ret.send(self.handle_delete(uuid).await);
|
||||
}
|
||||
Some(Snapshot { uuid, path, ret }) => {
|
||||
let _ = ret.send(self.handle_snapshot(uuid, path).await);
|
||||
Some(Snapshot { uuids, path, ret }) => {
|
||||
let _ = ret.send(self.handle_snapshot(uuids, path).await);
|
||||
}
|
||||
Some(Dump { uuid, path, ret }) => {
|
||||
let _ = ret.send(self.handle_dump(uuid, path).await);
|
||||
Some(Dump { uuids, path, ret }) => {
|
||||
let _ = ret.send(self.handle_dump(uuids, path).await);
|
||||
}
|
||||
Some(GetInfo { ret }) => {
|
||||
let _ = ret.send(self.handle_get_info().await);
|
||||
Some(GetSize { uuid, ret }) => {
|
||||
let _ = ret.send(self.handle_get_size(uuid).await);
|
||||
}
|
||||
None => break,
|
||||
}
|
||||
@ -199,51 +197,9 @@ where
|
||||
}
|
||||
|
||||
async fn handle_delete(&self, uuid: Uuid) -> Result<()> {
|
||||
let store = self.store.delete(uuid).await?;
|
||||
let store = self.store.clone();
|
||||
|
||||
if let Some(store) = store {
|
||||
tokio::task::spawn(async move {
|
||||
let store = get_arc_ownership_blocking(store).await;
|
||||
tokio::task::spawn_blocking(move || {
|
||||
store.prepare_for_closing().wait();
|
||||
info!("Update store {} was closed.", uuid);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_create(&self, uuid: Uuid) -> Result<()> {
|
||||
let _ = self.store.get_or_create(uuid).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_create(&self, uuid: Uuid) -> Result<()> {
|
||||
let _ = self.store.get_or_create(uuid).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_snapshot(&self, uuid: Uuid, path: PathBuf) -> Result<()> {
|
||||
let index_handle = self.index_handle.clone();
|
||||
if let Some(update_store) = self.store.get(uuid).await? {
|
||||
tokio::task::spawn_blocking(move || -> anyhow::Result<()> {
|
||||
// acquire write lock to prevent further writes during snapshot
|
||||
// the update lock must be acquired BEFORE the write lock to prevent dead lock
|
||||
let _lock = update_store.update_lock.lock();
|
||||
let mut txn = update_store.env.write_txn()?;
|
||||
|
||||
// create db snapshot
|
||||
update_store.snapshot(&mut txn, &path, uuid)?;
|
||||
|
||||
futures::executor::block_on(
|
||||
async move { index_handle.snapshot(uuid, path).await },
|
||||
)?;
|
||||
Ok(())
|
||||
})
|
||||
tokio::task::spawn_blocking(move || store.delete_all(uuid))
|
||||
.await
|
||||
.map_err(|e| UpdateError::Error(e.into()))?
|
||||
.map_err(|e| UpdateError::Error(e.into()))?;
|
||||
@ -280,6 +236,35 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_dump(&self, uuids: HashSet<Uuid>, path: PathBuf) -> Result<()> {
|
||||
let index_handle = self.index_handle.clone();
|
||||
let update_store = self.store.clone();
|
||||
tokio::task::spawn_blocking(move || -> anyhow::Result<()> {
|
||||
update_store.dump(&uuids, &path)?;
|
||||
|
||||
// Perform the snapshot of each index concurently. Only a third of the capabilities of
|
||||
// the index actor at a time not to put too much pressure on the index actor
|
||||
let path = &path;
|
||||
let handle = &index_handle;
|
||||
|
||||
let mut stream = futures::stream::iter(uuids.iter())
|
||||
.map(|&uuid| handle.dump(uuid, path.clone()))
|
||||
.buffer_unordered(CONCURRENT_INDEX_MSG / 3);
|
||||
|
||||
Handle::current().block_on(async {
|
||||
while let Some(res) = stream.next().await {
|
||||
res?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
.await
|
||||
.map_err(|e| UpdateError::Error(e.into()))?
|
||||
.map_err(|e| UpdateError::Error(e.into()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_get_info(&self) -> Result<UpdateStoreInfo> {
|
||||
let update_store = self.store.clone();
|
||||
let info = tokio::task::spawn_blocking(move || -> anyhow::Result<UpdateStoreInfo> {
|
||||
@ -292,42 +277,4 @@ where
|
||||
|
||||
Ok(info)
|
||||
}
|
||||
|
||||
async fn handle_dump(&self, uuid: Uuid, path: PathBuf) -> Result<()> {
|
||||
let index_handle = self.index_handle.clone();
|
||||
if let Some(update_store) = self.store.get(uuid).await? {
|
||||
tokio::task::spawn_blocking(move || -> anyhow::Result<()> {
|
||||
// acquire write lock to prevent further writes during the dump
|
||||
// the update lock must be acquired BEFORE the write lock to prevent dead lock
|
||||
let _lock = update_store.update_lock.lock();
|
||||
let mut txn = update_store.env.write_txn()?;
|
||||
|
||||
// create db dump
|
||||
update_store.dump(&mut txn, &path, uuid)?;
|
||||
|
||||
futures::executor::block_on(
|
||||
async move { index_handle.dump(uuid, path).await },
|
||||
)?;
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
.map_err(|e| UpdateError::Error(e.into()))?
|
||||
.map_err(|e| UpdateError::Error(e.into()))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_get_size(&self, uuid: Uuid) -> Result<u64> {
|
||||
let size = match self.store.get(uuid).await? {
|
||||
Some(update_store) => tokio::task::spawn_blocking(move || -> anyhow::Result<u64> {
|
||||
let txn = update_store.env.read_txn()?;
|
||||
|
||||
update_store.get_size(&txn)
|
||||
})
|
||||
.await
|
||||
.map_err(|e| UpdateError::Error(e.into()))?
|
||||
.map_err(|e| UpdateError::Error(e.into()))?,
|
||||
None => 0,
|
||||
};
|
||||
}
|
||||
|
@ -78,16 +78,9 @@ where
|
||||
receiver.await.expect("update actor killed.")
|
||||
}
|
||||
|
||||
async fn dump(&self, uuid: Uuid, path: PathBuf) -> Result<()> {
|
||||
async fn dump(&self, uuids: HashSet<Uuid>, path: PathBuf) -> Result<()> {
|
||||
let (ret, receiver) = oneshot::channel();
|
||||
let msg = UpdateMsg::Dump { uuid, path, ret };
|
||||
let _ = self.sender.send(msg).await;
|
||||
receiver.await.expect("update actor killed.")
|
||||
}
|
||||
|
||||
async fn get_size(&self, uuid: Uuid) -> Result<u64> {
|
||||
let (ret, receiver) = oneshot::channel();
|
||||
let msg = UpdateMsg::GetSize { uuid, ret };
|
||||
let msg = UpdateMsg::Dump { uuids, path, ret };
|
||||
let _ = self.sender.send(msg).await;
|
||||
receiver.await.expect("update actor killed.")
|
||||
}
|
||||
|
@ -32,15 +32,11 @@ pub enum UpdateMsg<D> {
|
||||
ret: oneshot::Sender<Result<()>>,
|
||||
},
|
||||
Dump {
|
||||
uuid: Uuid,
|
||||
uuids: HashSet<Uuid>,
|
||||
path: PathBuf,
|
||||
ret: oneshot::Sender<Result<()>>,
|
||||
},
|
||||
GetInfo {
|
||||
ret: oneshot::Sender<Result<UpdateStoreInfo>>,
|
||||
},
|
||||
GetSize {
|
||||
uuid: Uuid,
|
||||
ret: oneshot::Sender<Result<u64>>,
|
||||
},
|
||||
}
|
||||
|
@ -40,11 +40,9 @@ pub trait UpdateActorHandle {
|
||||
async fn get_all_updates_status(&self, uuid: Uuid) -> Result<Vec<UpdateStatus>>;
|
||||
async fn update_status(&self, uuid: Uuid, id: u64) -> Result<UpdateStatus>;
|
||||
async fn delete(&self, uuid: Uuid) -> Result<()>;
|
||||
async fn create(&self, uuid: Uuid) -> Result<()>;
|
||||
async fn snapshot(&self, uuid: Uuid, path: PathBuf) -> Result<()>;
|
||||
async fn dump(&self, uuid: Uuid, path: PathBuf) -> Result<()>;
|
||||
async fn snapshot(&self, uuid: HashSet<Uuid>, path: PathBuf) -> Result<()>;
|
||||
async fn dump(&self, uuid: HashSet<Uuid>, path: PathBuf) -> Result<()>;
|
||||
async fn get_info(&self) -> Result<UpdateStoreInfo>;
|
||||
async fn get_size(&self, uuid: Uuid) -> Result<u64>;
|
||||
async fn update(
|
||||
&self,
|
||||
meta: UpdateMeta,
|
||||
|
@ -499,31 +499,37 @@ impl UpdateStore {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn dump(
|
||||
&self,
|
||||
txn: &mut heed::RwTxn,
|
||||
path: impl AsRef<Path>,
|
||||
uuid: Uuid,
|
||||
) -> anyhow::Result<()> {
|
||||
pub fn dump(&self, uuids: &HashSet<Uuid>, path: impl AsRef<Path>) -> anyhow::Result<()> {
|
||||
let state_lock = self.state.write();
|
||||
state_lock.swap(State::Snapshoting); // TODO: rename the state
|
||||
|
||||
let txn = self.env.write_txn()?;
|
||||
|
||||
let update_path = path.as_ref().join("updates");
|
||||
create_dir_all(&update_path)?;
|
||||
|
||||
let mut dump_path = update_path.join(format!("update-{}", uuid));
|
||||
// acquire write lock to prevent further writes during dump
|
||||
create_dir_all(&dump_path)?;
|
||||
dump_path.push("data.mdb");
|
||||
create_dir_all(&update_path)?;
|
||||
let db_path = update_path.join("data.mdb");
|
||||
|
||||
// TODO: everything
|
||||
// create db dump
|
||||
self.env.copy_to_path(&dump_path, CompactionOption::Enabled)?;
|
||||
self.env.copy_to_path(&db_path, CompactionOption::Enabled)?;
|
||||
|
||||
let update_files_path = update_path.join("update_files");
|
||||
create_dir_all(&update_files_path)?;
|
||||
|
||||
for path in self.pending.iter(&txn)? {
|
||||
let (_, path) = path?;
|
||||
let name = path.file_name().unwrap();
|
||||
let to = update_files_path.join(name);
|
||||
copy(path, to)?;
|
||||
let pendings = self.pending_queue.iter(&txn)?.lazily_decode_data();
|
||||
|
||||
for entry in pendings {
|
||||
let ((_, uuid, _), pending) = entry?;
|
||||
if uuids.contains(&uuid) {
|
||||
if let Some(path) = pending.decode()?.content_path() {
|
||||
let name = path.file_name().unwrap();
|
||||
let to = update_files_path.join(name);
|
||||
copy(path, to)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -545,25 +551,6 @@ impl UpdateStore {
|
||||
|
||||
Ok(UpdateStoreInfo { size, processing })
|
||||
}
|
||||
|
||||
pub fn get_size(&self, txn: &heed::RoTxn) -> anyhow::Result<u64> {
|
||||
let mut size = self.env.size();
|
||||
let txn = self.env.read_txn()?;
|
||||
|
||||
for entry in self.pending_queue.iter(&txn)? {
|
||||
let (_, pending) = entry?;
|
||||
if let Some(path) = pending.content_path() {
|
||||
size += File::open(path)?.metadata()?.len();
|
||||
}
|
||||
}
|
||||
|
||||
let processing = match *self.state.read() {
|
||||
State::Processing(uuid, _) => Some(uuid),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
Ok(UpdateStoreInfo { size, processing })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
Reference in New Issue
Block a user