mirror of
https://github.com/meilisearch/MeiliSearch
synced 2025-04-18 07:57:59 +02:00
WIP
This commit is contained in:
parent
19f4c1ac98
commit
31bda976f2
@ -5,9 +5,10 @@ tasks affecting a single index into a [batch](crate::batch::Batch).
|
||||
The main function of the autobatcher is [`next_autobatch`].
|
||||
*/
|
||||
|
||||
use meilisearch_types::tasks::TaskId;
|
||||
use std::ops::ControlFlow::{self, Break, Continue};
|
||||
|
||||
use meilisearch_types::tasks::{BatchStopReason, TaskId};
|
||||
|
||||
use crate::KindWithContent;
|
||||
|
||||
/// Succinctly describes a task's [`Kind`](meilisearch_types::tasks::Kind)
|
||||
@ -147,14 +148,38 @@ impl BatchKind {
|
||||
task_id: TaskId,
|
||||
kind: KindWithContent,
|
||||
primary_key: Option<&str>,
|
||||
) -> (ControlFlow<BatchKind, BatchKind>, bool) {
|
||||
) -> (ControlFlow<(BatchKind, BatchStopReason), BatchKind>, bool) {
|
||||
use AutobatchKind as K;
|
||||
|
||||
match AutobatchKind::from(kind) {
|
||||
K::IndexCreation => (Break(BatchKind::IndexCreation { id: task_id }), true),
|
||||
K::IndexDeletion => (Break(BatchKind::IndexDeletion { ids: vec![task_id] }), false),
|
||||
K::IndexUpdate => (Break(BatchKind::IndexUpdate { id: task_id }), false),
|
||||
K::IndexSwap => (Break(BatchKind::IndexSwap { id: task_id }), false),
|
||||
K::IndexCreation => (
|
||||
Break((
|
||||
BatchKind::IndexCreation { id: task_id },
|
||||
BatchStopReason::TaskCannotBeBatched { kind, id: task_id },
|
||||
)),
|
||||
true,
|
||||
),
|
||||
K::IndexDeletion => (
|
||||
Break((
|
||||
BatchKind::IndexDeletion { ids: vec![task_id] },
|
||||
BatchStopReason::TaskCannotBeBatched { kind, id: task_id },
|
||||
)),
|
||||
false,
|
||||
),
|
||||
K::IndexUpdate => (
|
||||
Break((
|
||||
BatchKind::IndexUpdate { id: task_id },
|
||||
BatchStopReason::TaskCannotBeBatched { kind, id: task_id },
|
||||
)),
|
||||
false,
|
||||
),
|
||||
K::IndexSwap => (
|
||||
Break((
|
||||
BatchKind::IndexSwap { id: task_id },
|
||||
BatchStopReason::TaskCannotBeBatched { kind, id: task_id },
|
||||
)),
|
||||
false,
|
||||
),
|
||||
K::DocumentClear => (Continue(BatchKind::DocumentClear { ids: vec![task_id] }), false),
|
||||
K::DocumentImport { allow_index_creation, primary_key: pk }
|
||||
if primary_key.is_none() || pk.is_none() || primary_key == pk.as_deref() =>
|
||||
@ -169,15 +194,28 @@ impl BatchKind {
|
||||
)
|
||||
}
|
||||
// if the primary key set in the task was different than ours we should stop and make this batch fail asap.
|
||||
K::DocumentImport { allow_index_creation, primary_key } => (
|
||||
Break(BatchKind::DocumentOperation {
|
||||
K::DocumentImport { allow_index_creation, primary_key: pk } => (
|
||||
Break((
|
||||
BatchKind::DocumentOperation {
|
||||
allow_index_creation,
|
||||
primary_key,
|
||||
primary_key: pk,
|
||||
operation_ids: vec![task_id],
|
||||
}),
|
||||
},
|
||||
BatchStopReason::PrimaryKeyIndexMismatch {
|
||||
id: task_id,
|
||||
in_index: primary_key.unwrap().to_owned(),
|
||||
in_task: pk.unwrap(),
|
||||
},
|
||||
)),
|
||||
allow_index_creation,
|
||||
),
|
||||
K::DocumentEdition => (Break(BatchKind::DocumentEdition { id: task_id }), false),
|
||||
K::DocumentEdition => (
|
||||
Break((
|
||||
BatchKind::DocumentEdition { id: task_id },
|
||||
BatchStopReason::TaskCannotBeBatched { kind, id: task_id },
|
||||
)),
|
||||
false,
|
||||
),
|
||||
K::DocumentDeletion { by_filter: includes_by_filter } => (
|
||||
Continue(BatchKind::DocumentDeletion {
|
||||
deletion_ids: vec![task_id],
|
||||
@ -197,43 +235,40 @@ impl BatchKind {
|
||||
/// To ease the writing of the code. `true` can be returned when you don't need to create an index
|
||||
/// but false can't be returned if you needs to create an index.
|
||||
#[rustfmt::skip]
|
||||
fn accumulate(self, id: TaskId, kind: AutobatchKind, index_already_exists: bool, primary_key: Option<&str>) -> ControlFlow<BatchKind, BatchKind> {
|
||||
fn accumulate(self, id: TaskId, kind: AutobatchKind, index_already_exists: bool, primary_key: Option<&str>) -> ControlFlow<(BatchKind, BatchStopReason), BatchKind> {
|
||||
use AutobatchKind as K;
|
||||
|
||||
let pk: Option<String> = match (self.primary_key(), kind.primary_key(), primary_key) {
|
||||
// 1. If both task don't interact with primary key -> we can continue
|
||||
(batch_pk, None | Some(None), _) => {
|
||||
batch_pk.flatten().to_owned()
|
||||
},
|
||||
// 2.1 If we already have a primary-key ->
|
||||
// 2.1.1 If the task we're trying to accumulate have a pk it must be equal to our primary key
|
||||
(batch_pk, Some(Some(task_pk)), Some(index_pk)) => if task_pk == index_pk {
|
||||
Some(task_pk.to_owned())
|
||||
} else {
|
||||
return Break((this, BatchStopReason::PrimaryKeyMismatch { id, batch_pk: todo!(), task_pk: todo!() }))
|
||||
},
|
||||
// 2.2 If we don't have a primary-key ->
|
||||
// 2.2.2 If the batch is set to Some(None), the task should be too
|
||||
(Some(None), Some(None), None) => None,
|
||||
(Some(None), Some(Some(_)), None) => return Break((this, BatchStopReason::PrimaryKeyMismatch { id, batch_pk: todo!(), task_pk: todo!() })),
|
||||
(Some(Some(batch_pk)), Some(None), None) => Some(batch_pk.to_owned()),
|
||||
(Some(Some(batch_pk)), Some(Some(task_pk)), None) => if task_pk == batch_pk {
|
||||
Some(task_pk.to_owned())
|
||||
} else {
|
||||
return Break((this, BatchStopReason::PrimaryKeyMismatch { id, batch_pk: todo!(), task_pk: todo!() }))
|
||||
},
|
||||
(None, Some(Some(task_pk)), None) => Some(task_pk.to_owned())
|
||||
};
|
||||
|
||||
match (self, kind) {
|
||||
// We don't batch any of these operations
|
||||
(this, K::IndexCreation | K::IndexUpdate | K::IndexSwap | K::DocumentEdition) => Break(this),
|
||||
(this, K::IndexCreation | K::IndexUpdate | K::IndexSwap | K::DocumentEdition) => Break((this, BatchStopReason::TaskCannotBeBatched { kind, id })),
|
||||
// We must not batch tasks that don't have the same index creation rights if the index doesn't already exists.
|
||||
(this, kind) if !index_already_exists && this.allow_index_creation() == Some(false) && kind.allow_index_creation() == Some(true) => {
|
||||
Break(this)
|
||||
},
|
||||
// NOTE: We need to negate the whole condition since we're checking if we need to break instead of continue.
|
||||
// I wrote it this way because it's easier to understand than the other way around.
|
||||
(this, kind) if !(
|
||||
// 1. If both task don't interact with primary key -> we can continue
|
||||
(this.primary_key().is_none() && kind.primary_key().is_none()) ||
|
||||
// 2. Else ->
|
||||
(
|
||||
// 2.1 If we already have a primary-key ->
|
||||
(
|
||||
primary_key.is_some() &&
|
||||
// 2.1.1 If the task we're trying to accumulate have a pk it must be equal to our primary key
|
||||
// 2.1.2 If the task don't have a primary-key -> we can continue
|
||||
kind.primary_key().is_none_or(|pk| pk == primary_key)
|
||||
) ||
|
||||
// 2.2 If we don't have a primary-key ->
|
||||
(
|
||||
// 2.2.1 If both the batch and the task have a primary key they should be equal
|
||||
// 2.2.2 If the batch is set to Some(None), the task should be too
|
||||
// 2.2.3 If the batch is set to None -> we can continue
|
||||
this.primary_key().zip(kind.primary_key()).map_or(true, |(this, kind)| this == kind)
|
||||
)
|
||||
)
|
||||
|
||||
) // closing the negation
|
||||
|
||||
=> {
|
||||
Break(this)
|
||||
Break((this, BatchStopReason::IndexCreationMismatch { id }))
|
||||
},
|
||||
// The index deletion can batch with everything but must stop after
|
||||
(
|
||||
@ -244,7 +279,7 @@ impl BatchKind {
|
||||
K::IndexDeletion,
|
||||
) => {
|
||||
ids.push(id);
|
||||
Break(BatchKind::IndexDeletion { ids })
|
||||
Break((BatchKind::IndexDeletion { ids }, BatchStopReason::IndexDeletion { id }))
|
||||
}
|
||||
(
|
||||
BatchKind::ClearAndSettings { settings_ids: mut ids, allow_index_creation: _, mut other },
|
||||
@ -252,7 +287,7 @@ impl BatchKind {
|
||||
) => {
|
||||
ids.push(id);
|
||||
ids.append(&mut other);
|
||||
Break(BatchKind::IndexDeletion { ids })
|
||||
Break((BatchKind::IndexDeletion { ids }, BatchStopReason::IndexDeletion { id }))
|
||||
}
|
||||
|
||||
(
|
||||
@ -265,7 +300,7 @@ impl BatchKind {
|
||||
(
|
||||
this @ BatchKind::DocumentClear { .. },
|
||||
K::DocumentImport { .. } | K::Settings { .. },
|
||||
) => Break(this),
|
||||
) => Break((this, BatchStopReason::DocumentOperationWithSettings { id })),
|
||||
(
|
||||
BatchKind::DocumentOperation { allow_index_creation: _, primary_key: _, mut operation_ids },
|
||||
K::DocumentClear,
|
||||
@ -277,7 +312,7 @@ impl BatchKind {
|
||||
// we can autobatch different kind of document operations and mix replacements with updates
|
||||
(
|
||||
BatchKind::DocumentOperation { allow_index_creation, primary_key: _, mut operation_ids },
|
||||
K::DocumentImport { primary_key: pk, .. },
|
||||
K::DocumentImport { primary_key, .. },
|
||||
) => {
|
||||
operation_ids.push(id);
|
||||
Continue(BatchKind::DocumentOperation {
|
||||
@ -287,15 +322,15 @@ impl BatchKind {
|
||||
})
|
||||
}
|
||||
(
|
||||
BatchKind::DocumentOperation { allow_index_creation, primary_key, mut operation_ids },
|
||||
BatchKind::DocumentOperation { allow_index_creation, primary_key: _, mut operation_ids },
|
||||
K::DocumentDeletion { by_filter: false },
|
||||
) => {
|
||||
operation_ids.push(id);
|
||||
|
||||
Continue(BatchKind::DocumentOperation {
|
||||
allow_index_creation,
|
||||
primary_key,
|
||||
operation_ids,
|
||||
primary_key: pk,
|
||||
})
|
||||
}
|
||||
// We can't batch a document operation with a delete by filter
|
||||
@ -303,12 +338,12 @@ impl BatchKind {
|
||||
this @ BatchKind::DocumentOperation { .. },
|
||||
K::DocumentDeletion { by_filter: true },
|
||||
) => {
|
||||
Break(this)
|
||||
Break((this, BatchStopReason::DocumentOperationWithDeletionByFilter { id }))
|
||||
}
|
||||
(
|
||||
this @ BatchKind::DocumentOperation { .. },
|
||||
K::Settings { .. },
|
||||
) => Break(this),
|
||||
) => Break((this, BatchStopReason::DocumentOperationWithSettings { id })),
|
||||
|
||||
(BatchKind::DocumentDeletion { mut deletion_ids, includes_by_filter: _ }, K::DocumentClear) => {
|
||||
deletion_ids.push(id);
|
||||
@ -318,7 +353,7 @@ impl BatchKind {
|
||||
(
|
||||
this @ BatchKind::DocumentDeletion { deletion_ids: _, includes_by_filter: true },
|
||||
K::DocumentImport { .. }
|
||||
) => Break(this),
|
||||
) => Break((this, BatchStopReason::DeletionByFilterWithDocumentOperation { id })),
|
||||
// we can autobatch the deletion and import if the index already exists
|
||||
(
|
||||
BatchKind::DocumentDeletion { mut deletion_ids, includes_by_filter: false },
|
||||
@ -345,18 +380,18 @@ impl BatchKind {
|
||||
operation_ids: deletion_ids,
|
||||
})
|
||||
}
|
||||
// we can't autobatch a deletion and an import if the index does not exists but would be created by an addition
|
||||
// we can't autobatch a deletion and an import if the index does not exist but would be created by an addition
|
||||
(
|
||||
this @ BatchKind::DocumentDeletion { .. },
|
||||
K::DocumentImport { .. }
|
||||
) => {
|
||||
Break(this)
|
||||
Break((this, BatchStopReason::IndexCreationMismatch { id }))
|
||||
}
|
||||
(BatchKind::DocumentDeletion { mut deletion_ids, includes_by_filter }, K::DocumentDeletion { by_filter }) => {
|
||||
deletion_ids.push(id);
|
||||
Continue(BatchKind::DocumentDeletion { deletion_ids, includes_by_filter: includes_by_filter | by_filter })
|
||||
}
|
||||
(this @ BatchKind::DocumentDeletion { .. }, K::Settings { .. }) => Break(this),
|
||||
(this @ BatchKind::DocumentDeletion { .. }, K::Settings { .. }) => Break((this, BatchStopReason::DocumentOperationWithSettings { id })),
|
||||
|
||||
(
|
||||
BatchKind::Settings { settings_ids, allow_index_creation },
|
||||
@ -369,7 +404,7 @@ impl BatchKind {
|
||||
(
|
||||
this @ BatchKind::Settings { .. },
|
||||
K::DocumentImport { .. } | K::DocumentDeletion { .. },
|
||||
) => Break(this),
|
||||
) => Break((this, BatchStopReason::SettingsWithDocumentOperation { id })),
|
||||
(
|
||||
BatchKind::Settings { mut settings_ids, allow_index_creation },
|
||||
K::Settings { .. },
|
||||
@ -448,7 +483,7 @@ pub fn autobatch(
|
||||
enqueued: Vec<(TaskId, KindWithContent)>,
|
||||
index_already_exists: bool,
|
||||
primary_key: Option<&str>,
|
||||
) -> Option<(BatchKind, bool)> {
|
||||
) -> Option<(BatchKind, bool, Option<BatchStopReason>)> {
|
||||
let mut enqueued = enqueued.into_iter();
|
||||
let (id, kind) = enqueued.next()?;
|
||||
|
||||
@ -457,7 +492,9 @@ pub fn autobatch(
|
||||
|
||||
let (mut acc, must_create_index) = match BatchKind::new(id, kind, primary_key) {
|
||||
(Continue(acc), create) => (acc, create),
|
||||
(Break(acc), create) => return Some((acc, create)),
|
||||
(Break((acc, batch_stop_reason)), create) => {
|
||||
return Some((acc, create, Some(batch_stop_reason)))
|
||||
}
|
||||
};
|
||||
|
||||
// if an index has been created in the previous step we can consider it as existing.
|
||||
@ -466,9 +503,11 @@ pub fn autobatch(
|
||||
for (id, kind) in enqueued {
|
||||
acc = match acc.accumulate(id, kind.into(), index_exist, primary_key) {
|
||||
Continue(acc) => acc,
|
||||
Break(acc) => return Some((acc, must_create_index)),
|
||||
Break((acc, batch_stop_reason)) => {
|
||||
return Some((acc, must_create_index, Some(batch_stop_reason)))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Some((acc, must_create_index))
|
||||
Some((acc, must_create_index, None))
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use std::fmt;
|
||||
use meilisearch_types::heed::RoTxn;
|
||||
use meilisearch_types::milli::update::IndexDocumentsMethod;
|
||||
use meilisearch_types::settings::{Settings, Unchecked};
|
||||
use meilisearch_types::tasks::{Kind, KindWithContent, Status, Task};
|
||||
use meilisearch_types::tasks::{BatchStopReason, Kind, KindWithContent, Status, Task};
|
||||
use roaring::RoaringBitmap;
|
||||
use uuid::Uuid;
|
||||
|
||||
@ -440,6 +440,7 @@ impl IndexScheduler {
|
||||
let mut current_batch = ProcessingBatch::new(batch_id);
|
||||
|
||||
let enqueued = &self.queue.tasks.get_status(rtxn, Status::Enqueued)?;
|
||||
let count_total_enqueued = enqueued.len();
|
||||
let failed = &self.queue.tasks.get_status(rtxn, Status::Failed)?;
|
||||
|
||||
// 0. The priority over everything is to upgrade the instance
|
||||
@ -453,6 +454,10 @@ impl IndexScheduler {
|
||||
current_batch.uid = batch_uid;
|
||||
}
|
||||
current_batch.processing(&mut tasks);
|
||||
current_batch.reason(BatchStopReason::TaskCannotBeBatched {
|
||||
kind: Kind::UpgradeDatabase,
|
||||
id: tasks.last().unwrap(),
|
||||
});
|
||||
return Ok(Some((Batch::UpgradeDatabase { tasks }, current_batch)));
|
||||
}
|
||||
|
||||
@ -462,6 +467,10 @@ impl IndexScheduler {
|
||||
let mut task =
|
||||
self.queue.tasks.get_task(rtxn, task_id)?.ok_or(Error::CorruptedTaskQueue)?;
|
||||
current_batch.processing(Some(&mut task));
|
||||
current_batch.reason(BatchStopReason::TaskCannotBeBatched {
|
||||
kind: Kind::TaskCancelation,
|
||||
id: tasks.last().unwrap(),
|
||||
});
|
||||
return Ok(Some((Batch::TaskCancelation { task }, current_batch)));
|
||||
}
|
||||
|
||||
@ -470,6 +479,10 @@ impl IndexScheduler {
|
||||
if !to_delete.is_empty() {
|
||||
let mut tasks = self.queue.tasks.get_existing_tasks(rtxn, to_delete)?;
|
||||
current_batch.processing(&mut tasks);
|
||||
current_batch.reason(BatchStopReason::TaskCannotBeBatched {
|
||||
kind: Kind::TaskDeletion,
|
||||
id: tasks.last().unwrap(),
|
||||
});
|
||||
return Ok(Some((Batch::TaskDeletions(tasks), current_batch)));
|
||||
}
|
||||
|
||||
@ -478,6 +491,10 @@ impl IndexScheduler {
|
||||
if !to_snapshot.is_empty() {
|
||||
let mut tasks = self.queue.tasks.get_existing_tasks(rtxn, to_snapshot)?;
|
||||
current_batch.processing(&mut tasks);
|
||||
current_batch.reason(BatchStopReason::TaskCannotBeBatched {
|
||||
kind: Kind::SnapshotCreation,
|
||||
id: tasks.last().unwrap(),
|
||||
});
|
||||
return Ok(Some((Batch::SnapshotCreation(tasks), current_batch)));
|
||||
}
|
||||
|
||||
@ -487,6 +504,10 @@ impl IndexScheduler {
|
||||
let mut task =
|
||||
self.queue.tasks.get_task(rtxn, to_dump)?.ok_or(Error::CorruptedTaskQueue)?;
|
||||
current_batch.processing(Some(&mut task));
|
||||
current_batch.reason(BatchStopReason::TaskCannotBeBatched {
|
||||
kind: Kind::DumpCreation,
|
||||
id: tasks.last().unwrap(),
|
||||
});
|
||||
return Ok(Some((Batch::Dump(task), current_batch)));
|
||||
}
|
||||
|
||||
@ -504,6 +525,10 @@ impl IndexScheduler {
|
||||
} else {
|
||||
assert!(matches!(&task.kind, KindWithContent::IndexSwap { swaps } if swaps.is_empty()));
|
||||
current_batch.processing(Some(&mut task));
|
||||
current_batch.reason(BatchStopReason::TaskCannotBeBatched {
|
||||
kind: Kind::IndexSwap,
|
||||
id: tasks.last().unwrap(),
|
||||
});
|
||||
return Ok(Some((Batch::IndexSwap { task }, current_batch)));
|
||||
};
|
||||
|
||||
@ -525,9 +550,14 @@ impl IndexScheduler {
|
||||
1
|
||||
};
|
||||
|
||||
let mut stop_reason = BatchStopReason::default();
|
||||
let mut enqueued = Vec::new();
|
||||
let mut total_size: u64 = 0;
|
||||
for task_id in index_tasks.into_iter().take(tasks_limit) {
|
||||
for task_id in index_tasks.into_iter() {
|
||||
if enqueued.len() >= task_limit {
|
||||
stop_reason = BatchStopReason::ReachedTaskLimit { task_limit };
|
||||
break;
|
||||
}
|
||||
let task = self
|
||||
.queue
|
||||
.tasks
|
||||
@ -539,16 +569,27 @@ impl IndexScheduler {
|
||||
total_size = total_size.saturating_add(content_size);
|
||||
}
|
||||
|
||||
if total_size > self.scheduler.batched_tasks_size_limit && !enqueued.is_empty() {
|
||||
let size_limit = self.scheduler.batched_tasks_size_limit;
|
||||
if total_size > size_limit && !enqueued.is_empty() {
|
||||
stop_reason = BatchStopReason::ReachedSizeLimit { size_limit, size: total_size };
|
||||
break;
|
||||
}
|
||||
|
||||
enqueued.push((task.uid, task.kind));
|
||||
}
|
||||
|
||||
if let Some((batchkind, create_index)) =
|
||||
stop_reason.replace_unspecified({
|
||||
if enqueued.len() == count_total_enqueued as usize {
|
||||
BatchStopReason::ExhaustedEnqueuedTasks
|
||||
} else {
|
||||
BatchStopReason::ExhaustedEnqueuedTasksForIndex { index: index_name.to_owned() }
|
||||
}
|
||||
});
|
||||
|
||||
if let Some((batchkind, create_index, autobatch_stop_reason)) =
|
||||
autobatcher::autobatch(enqueued, index_already_exists, primary_key.as_deref())
|
||||
{
|
||||
current_batch.reason(autobatch_stop_reason.unwrap_or(stop_reason));
|
||||
return Ok(self
|
||||
.create_next_batch_index(
|
||||
rtxn,
|
||||
|
@ -7,7 +7,9 @@ use meilisearch_types::batches::{Batch, BatchEnqueuedAt, BatchId, BatchStats};
|
||||
use meilisearch_types::heed::{Database, RoTxn, RwTxn};
|
||||
use meilisearch_types::milli::CboRoaringBitmapCodec;
|
||||
use meilisearch_types::task_view::DetailsView;
|
||||
use meilisearch_types::tasks::{Details, IndexSwap, Kind, KindWithContent, Status};
|
||||
use meilisearch_types::tasks::{
|
||||
BatchStopReason, Details, IndexSwap, Kind, KindWithContent, Status,
|
||||
};
|
||||
use roaring::RoaringBitmap;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
@ -33,6 +35,7 @@ pub struct ProcessingBatch {
|
||||
pub enqueued_at: Option<BatchEnqueuedAt>,
|
||||
pub started_at: OffsetDateTime,
|
||||
pub finished_at: Option<OffsetDateTime>,
|
||||
pub reason: BatchStopReason,
|
||||
}
|
||||
|
||||
impl ProcessingBatch {
|
||||
@ -53,6 +56,7 @@ impl ProcessingBatch {
|
||||
enqueued_at: None,
|
||||
started_at: OffsetDateTime::now_utc(),
|
||||
finished_at: None,
|
||||
reason: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,6 +97,10 @@ impl ProcessingBatch {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reason(&mut self, reason: BatchStopReason) {
|
||||
self.reason = reason;
|
||||
}
|
||||
|
||||
/// Must be called once the batch has finished processing.
|
||||
pub fn finished(&mut self) {
|
||||
self.details = DetailsView::default();
|
||||
@ -141,6 +149,7 @@ impl ProcessingBatch {
|
||||
started_at: self.started_at,
|
||||
finished_at: self.finished_at,
|
||||
enqueued_at: self.enqueued_at,
|
||||
stop_reason: self.reason.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use time::OffsetDateTime;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::task_view::DetailsView;
|
||||
use crate::tasks::{Kind, Status};
|
||||
use crate::tasks::{BatchStopReason, Kind, Status};
|
||||
|
||||
pub type BatchId = u32;
|
||||
|
||||
@ -28,11 +28,26 @@ pub struct Batch {
|
||||
// Enqueued at is never displayed and is only required when removing a batch.
|
||||
// It's always some except when upgrading from a database pre v1.12
|
||||
pub enqueued_at: Option<BatchEnqueuedAt>,
|
||||
#[serde(default = "default_stop_reason")]
|
||||
pub stop_reason: String,
|
||||
}
|
||||
|
||||
fn default_stop_reason() -> String {
|
||||
BatchStopReason::default().to_string()
|
||||
}
|
||||
|
||||
impl PartialEq for Batch {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
let Self { uid, progress, details, stats, started_at, finished_at, enqueued_at } = self;
|
||||
let Self {
|
||||
uid,
|
||||
progress,
|
||||
details,
|
||||
stats,
|
||||
started_at,
|
||||
finished_at,
|
||||
enqueued_at,
|
||||
stop_reason,
|
||||
} = self;
|
||||
|
||||
*uid == other.uid
|
||||
&& progress.is_none() == other.progress.is_none()
|
||||
@ -41,6 +56,7 @@ impl PartialEq for Batch {
|
||||
&& started_at == &other.started_at
|
||||
&& finished_at == &other.finished_at
|
||||
&& enqueued_at == &other.enqueued_at
|
||||
&& stop_reason == &other.stop_reason
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -675,6 +675,121 @@ impl Details {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub enum BatchStopReason {
|
||||
#[default]
|
||||
Unspecified,
|
||||
TaskCannotBeBatched {
|
||||
kind: Kind,
|
||||
id: TaskId,
|
||||
},
|
||||
ExhaustedEnqueuedTasks,
|
||||
ExhaustedEnqueuedTasksForIndex {
|
||||
index: String,
|
||||
},
|
||||
ReachedTaskLimit {
|
||||
task_limit: usize,
|
||||
},
|
||||
ReachedSizeLimit {
|
||||
size_limit: usize,
|
||||
size: usize,
|
||||
},
|
||||
PrimaryKeyIndexMismatch {
|
||||
id: TaskId,
|
||||
in_index: String,
|
||||
in_task: String,
|
||||
},
|
||||
IndexCreationMismatch {
|
||||
id: TaskId,
|
||||
},
|
||||
PrimaryKeyMismatch {
|
||||
id: TaskId,
|
||||
batch_pk: Option<String>,
|
||||
task_pk: Option<String>,
|
||||
},
|
||||
IndexDeletion {
|
||||
id: TaskId,
|
||||
},
|
||||
DocumentOperationWithSettings {
|
||||
id: TaskId,
|
||||
},
|
||||
DocumentOperationWithDeletionByFilter {
|
||||
id: TaskId,
|
||||
},
|
||||
DeletionByFilterWithDocumentOperation {
|
||||
id: TaskId,
|
||||
},
|
||||
SettingsWithDocumentOperation {
|
||||
id: TaskId,
|
||||
},
|
||||
}
|
||||
|
||||
impl BatchStopReason {
|
||||
pub fn replace_unspecified(&mut self, new: BatchStopReason) {
|
||||
if let BatchStopReason::Unspecified = self {
|
||||
*self = new;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum PrimaryKeyMismatchReason {}
|
||||
|
||||
impl Display for BatchStopReason {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
BatchStopReason::Unspecified => f.write_str("unspecified"),
|
||||
BatchStopReason::TaskCannotBeBatched { kind, id } => {
|
||||
write!(f, "task with id {id} of type `{kind}` cannot be batched")
|
||||
}
|
||||
BatchStopReason::ExhaustedEnqueuedTasks => f.write_str("batched all enqueued tasks"),
|
||||
BatchStopReason::ExhaustedEnqueuedTasksForIndex { index } => {
|
||||
write!(f, "batched all enqueued tasks for index `{index}`")
|
||||
}
|
||||
BatchStopReason::ReachedTaskLimit { task_limit } => {
|
||||
write!(f, "reached configured batch limit of {task_limit} tasks")
|
||||
}
|
||||
BatchStopReason::ReachedSizeLimit { size_limit, size } => write!(
|
||||
f,
|
||||
"reached configured batch size limit of {size_limit}B with a total of {size}B"
|
||||
),
|
||||
BatchStopReason::PrimaryKeyIndexMismatch { id, in_index, in_task } => {
|
||||
write!(f, "primary key `{in_task}` in task with id {id} is different from the primary key of the index `{in_index}`")
|
||||
}
|
||||
BatchStopReason::IndexCreationMismatch { id } => {
|
||||
write!(f, "task with id {id} has different index creation rules as in the batch")
|
||||
}
|
||||
BatchStopReason::PrimaryKeyMismatch { id, batch_pk, task_pk } => {}
|
||||
BatchStopReason::IndexDeletion { id } => {
|
||||
write!(f, "task with id {id} deletes the index")
|
||||
}
|
||||
BatchStopReason::DocumentOperationWithSettings { id } => {
|
||||
write!(
|
||||
f,
|
||||
"task with id {id} is a settings change in a batch of document operations"
|
||||
)
|
||||
}
|
||||
BatchStopReason::DocumentOperationWithDeletionByFilter { id } => {
|
||||
write!(
|
||||
f,
|
||||
"task with id {id} is a deletion by filter in a batch of document operations"
|
||||
)
|
||||
}
|
||||
BatchStopReason::DeletionByFilterWithDocumentOperation { id } => {
|
||||
write!(
|
||||
f,
|
||||
"task with id {id} is a document operation in a batch of deletions by filter"
|
||||
)
|
||||
}
|
||||
BatchStopReason::SettingsWithDocumentOperation { id } => {
|
||||
write!(
|
||||
f,
|
||||
"task with id {id} is a document operation in a batch of settings changes"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Serialize a `time::Duration` as a best effort ISO 8601 while waiting for
|
||||
/// https://github.com/time-rs/time/issues/378.
|
||||
/// This code is a port of the old code of time that was removed in 0.2.
|
||||
|
Loading…
x
Reference in New Issue
Block a user