2022-09-06 22:43:59 +08:00
|
|
|
use anyhow::Result;
|
2022-09-14 04:38:43 +08:00
|
|
|
use index::{Settings, Unchecked};
|
2022-09-14 19:13:44 +08:00
|
|
|
|
2022-09-14 22:16:53 +08:00
|
|
|
use serde::{Deserialize, Serialize, Serializer};
|
|
|
|
use std::{fmt::Write, path::PathBuf};
|
|
|
|
use time::{Duration, OffsetDateTime};
|
2022-09-08 04:16:49 +08:00
|
|
|
use uuid::Uuid;
|
2022-09-06 22:43:59 +08:00
|
|
|
|
|
|
|
use crate::TaskId;
|
|
|
|
|
2022-09-08 02:08:07 +08:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
2022-09-06 22:43:59 +08:00
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub enum Status {
|
|
|
|
Enqueued,
|
|
|
|
Processing,
|
|
|
|
Succeeded,
|
|
|
|
Failed,
|
|
|
|
}
|
|
|
|
|
2022-09-14 22:16:53 +08:00
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct Error {
|
|
|
|
message: String,
|
|
|
|
code: String,
|
|
|
|
#[serde(rename = "type")]
|
|
|
|
kind: String,
|
|
|
|
link: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct TaskView {
|
|
|
|
pub uid: TaskId,
|
|
|
|
pub index_uid: Option<String>,
|
|
|
|
pub status: Status,
|
|
|
|
#[serde(rename = "type")]
|
|
|
|
pub kind: Kind,
|
|
|
|
|
|
|
|
pub details: Option<Details>,
|
|
|
|
pub error: Option<Error>,
|
|
|
|
|
|
|
|
#[serde(serialize_with = "serialize_duration")]
|
|
|
|
pub duration: Option<Duration>,
|
|
|
|
#[serde(with = "time::serde::rfc3339")]
|
|
|
|
pub enqueued_at: OffsetDateTime,
|
|
|
|
#[serde(with = "time::serde::rfc3339::option")]
|
|
|
|
pub started_at: Option<OffsetDateTime>,
|
|
|
|
#[serde(with = "time::serde::rfc3339::option")]
|
|
|
|
pub finished_at: Option<OffsetDateTime>,
|
|
|
|
}
|
|
|
|
|
2022-09-06 22:43:59 +08:00
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct Task {
|
2022-09-08 02:08:07 +08:00
|
|
|
pub uid: TaskId,
|
|
|
|
|
2022-09-13 17:46:07 +08:00
|
|
|
#[serde(with = "time::serde::rfc3339")]
|
|
|
|
pub enqueued_at: OffsetDateTime,
|
2022-09-06 22:43:59 +08:00
|
|
|
#[serde(with = "time::serde::rfc3339::option")]
|
|
|
|
pub started_at: Option<OffsetDateTime>,
|
|
|
|
#[serde(with = "time::serde::rfc3339::option")]
|
|
|
|
pub finished_at: Option<OffsetDateTime>,
|
|
|
|
|
2022-09-14 22:16:53 +08:00
|
|
|
pub error: Option<Error>,
|
|
|
|
pub details: Option<Details>,
|
2022-09-09 07:09:50 +08:00
|
|
|
|
2022-09-06 22:43:59 +08:00
|
|
|
pub status: Status,
|
2022-09-07 05:49:19 +08:00
|
|
|
pub kind: KindWithContent,
|
2022-09-06 22:43:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Task {
|
2022-09-07 06:22:58 +08:00
|
|
|
/// Persist all the temp files associated with the task.
|
2022-09-06 22:43:59 +08:00
|
|
|
pub fn persist(&self) -> Result<()> {
|
|
|
|
self.kind.persist()
|
|
|
|
}
|
|
|
|
|
2022-09-07 06:22:58 +08:00
|
|
|
/// Delete all the files associated with the task.
|
2022-09-06 22:43:59 +08:00
|
|
|
pub fn remove_data(&self) -> Result<()> {
|
|
|
|
self.kind.remove_data()
|
|
|
|
}
|
2022-09-07 06:22:58 +08:00
|
|
|
|
|
|
|
/// Return the list of indexes updated by this tasks.
|
|
|
|
pub fn indexes(&self) -> Option<Vec<&str>> {
|
|
|
|
self.kind.indexes()
|
|
|
|
}
|
2022-09-14 22:16:53 +08:00
|
|
|
|
|
|
|
/// Convert a Task to a TaskView
|
|
|
|
pub fn as_task_view(&self) -> TaskView {
|
|
|
|
TaskView {
|
|
|
|
uid: self.uid,
|
|
|
|
index_uid: self
|
|
|
|
.indexes()
|
|
|
|
.and_then(|vec| vec.first().map(|i| i.to_string())),
|
|
|
|
status: self.status,
|
|
|
|
kind: self.kind.as_kind(),
|
|
|
|
details: self.details.clone(),
|
|
|
|
error: self.error.clone(),
|
|
|
|
duration: self
|
|
|
|
.started_at
|
|
|
|
.zip(self.finished_at)
|
|
|
|
.map(|(start, end)| end - start),
|
|
|
|
enqueued_at: self.enqueued_at,
|
|
|
|
started_at: self.started_at,
|
|
|
|
finished_at: self.finished_at,
|
|
|
|
}
|
|
|
|
}
|
2022-09-06 22:43:59 +08:00
|
|
|
}
|
|
|
|
|
2022-09-14 04:38:43 +08:00
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
2022-09-06 22:43:59 +08:00
|
|
|
#[serde(rename_all = "camelCase")]
|
2022-09-07 05:49:19 +08:00
|
|
|
pub enum KindWithContent {
|
2022-09-14 06:34:02 +08:00
|
|
|
DocumentAddition {
|
|
|
|
index_uid: String,
|
|
|
|
primary_key: Option<String>,
|
|
|
|
content_file: Uuid,
|
|
|
|
documents_count: usize,
|
|
|
|
allow_index_creation: bool,
|
|
|
|
},
|
|
|
|
DocumentUpdate {
|
2022-09-14 04:38:43 +08:00
|
|
|
index_uid: String,
|
2022-09-13 17:46:07 +08:00
|
|
|
primary_key: Option<String>,
|
2022-09-08 04:16:49 +08:00
|
|
|
content_file: Uuid,
|
2022-09-14 04:38:43 +08:00
|
|
|
documents_count: usize,
|
|
|
|
allow_index_creation: bool,
|
2022-09-06 22:43:59 +08:00
|
|
|
},
|
|
|
|
DocumentDeletion {
|
2022-09-14 04:38:43 +08:00
|
|
|
index_uid: String,
|
2022-09-06 22:43:59 +08:00
|
|
|
documents_ids: Vec<String>,
|
|
|
|
},
|
2022-09-14 04:38:43 +08:00
|
|
|
DocumentClear {
|
|
|
|
index_uid: String,
|
2022-09-06 22:43:59 +08:00
|
|
|
},
|
2022-09-13 17:46:07 +08:00
|
|
|
Settings {
|
2022-09-14 04:38:43 +08:00
|
|
|
index_uid: String,
|
|
|
|
new_settings: Settings<Unchecked>,
|
|
|
|
is_deletion: bool,
|
|
|
|
allow_index_creation: bool,
|
2022-09-13 17:46:07 +08:00
|
|
|
},
|
2022-09-14 04:38:43 +08:00
|
|
|
IndexDeletion {
|
|
|
|
index_uid: String,
|
2022-09-06 22:43:59 +08:00
|
|
|
},
|
2022-09-14 04:38:43 +08:00
|
|
|
IndexCreation {
|
|
|
|
index_uid: String,
|
2022-09-06 22:43:59 +08:00
|
|
|
primary_key: Option<String>,
|
|
|
|
},
|
2022-09-14 04:38:43 +08:00
|
|
|
IndexUpdate {
|
|
|
|
index_uid: String,
|
|
|
|
primary_key: Option<String>,
|
|
|
|
},
|
|
|
|
IndexRename {
|
|
|
|
index_uid: String,
|
|
|
|
new_name: String,
|
2022-09-06 22:43:59 +08:00
|
|
|
},
|
2022-09-14 04:38:43 +08:00
|
|
|
IndexSwap {
|
2022-09-06 22:43:59 +08:00
|
|
|
lhs: String,
|
|
|
|
rhs: String,
|
|
|
|
},
|
|
|
|
CancelTask {
|
|
|
|
tasks: Vec<TaskId>,
|
|
|
|
},
|
2022-09-14 04:38:43 +08:00
|
|
|
DumpExport {
|
|
|
|
output: PathBuf,
|
|
|
|
},
|
|
|
|
Snapshot,
|
2022-09-06 22:43:59 +08:00
|
|
|
}
|
|
|
|
|
2022-09-07 05:49:19 +08:00
|
|
|
impl KindWithContent {
|
|
|
|
pub fn as_kind(&self) -> Kind {
|
|
|
|
match self {
|
2022-09-14 06:34:02 +08:00
|
|
|
KindWithContent::DocumentAddition { .. } => Kind::DocumentAddition,
|
|
|
|
KindWithContent::DocumentUpdate { .. } => Kind::DocumentUpdate,
|
2022-09-07 05:49:19 +08:00
|
|
|
KindWithContent::DocumentDeletion { .. } => Kind::DocumentDeletion,
|
2022-09-14 04:38:43 +08:00
|
|
|
KindWithContent::DocumentClear { .. } => Kind::DocumentClear,
|
|
|
|
KindWithContent::Settings { .. } => Kind::Settings,
|
|
|
|
KindWithContent::IndexCreation { .. } => Kind::IndexCreation,
|
|
|
|
KindWithContent::IndexDeletion { .. } => Kind::IndexDeletion,
|
|
|
|
KindWithContent::IndexUpdate { .. } => Kind::IndexUpdate,
|
|
|
|
KindWithContent::IndexRename { .. } => Kind::IndexRename,
|
|
|
|
KindWithContent::IndexSwap { .. } => Kind::IndexSwap,
|
2022-09-07 05:49:19 +08:00
|
|
|
KindWithContent::CancelTask { .. } => Kind::CancelTask,
|
2022-09-14 04:38:43 +08:00
|
|
|
KindWithContent::DumpExport { .. } => Kind::DumpExport,
|
2022-09-07 05:49:19 +08:00
|
|
|
KindWithContent::Snapshot => Kind::Snapshot,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-06 22:43:59 +08:00
|
|
|
pub fn persist(&self) -> Result<()> {
|
2022-09-07 06:22:58 +08:00
|
|
|
use KindWithContent::*;
|
|
|
|
|
2022-09-06 22:43:59 +08:00
|
|
|
match self {
|
2022-09-14 06:34:02 +08:00
|
|
|
DocumentAddition { .. } | DocumentUpdate { .. } => {
|
2022-09-06 22:43:59 +08:00
|
|
|
// TODO: TAMO: persist the file
|
|
|
|
// content_file.persist();
|
|
|
|
Ok(())
|
|
|
|
}
|
2022-09-14 04:38:43 +08:00
|
|
|
DocumentDeletion { .. }
|
|
|
|
| DocumentClear { .. }
|
2022-09-13 17:46:07 +08:00
|
|
|
| Settings { .. }
|
2022-09-14 04:38:43 +08:00
|
|
|
| IndexCreation { .. }
|
|
|
|
| IndexDeletion { .. }
|
|
|
|
| IndexUpdate { .. }
|
|
|
|
| IndexRename { .. }
|
|
|
|
| IndexSwap { .. }
|
2022-09-07 06:22:58 +08:00
|
|
|
| CancelTask { .. }
|
2022-09-14 04:38:43 +08:00
|
|
|
| DumpExport { .. }
|
|
|
|
| Snapshot => Ok(()), // There is nothing to persist for all these tasks
|
2022-09-06 22:43:59 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn remove_data(&self) -> Result<()> {
|
2022-09-07 06:22:58 +08:00
|
|
|
use KindWithContent::*;
|
|
|
|
|
2022-09-06 22:43:59 +08:00
|
|
|
match self {
|
2022-09-14 06:34:02 +08:00
|
|
|
DocumentAddition { .. } | DocumentUpdate { .. } => {
|
2022-09-06 22:43:59 +08:00
|
|
|
// TODO: TAMO: delete the file
|
|
|
|
// content_file.delete();
|
|
|
|
Ok(())
|
|
|
|
}
|
2022-09-14 06:34:02 +08:00
|
|
|
IndexCreation { .. }
|
2022-09-07 06:22:58 +08:00
|
|
|
| DocumentDeletion { .. }
|
2022-09-14 04:38:43 +08:00
|
|
|
| DocumentClear { .. }
|
|
|
|
| Settings { .. }
|
|
|
|
| IndexDeletion { .. }
|
|
|
|
| IndexUpdate { .. }
|
|
|
|
| IndexRename { .. }
|
|
|
|
| IndexSwap { .. }
|
2022-09-07 06:22:58 +08:00
|
|
|
| CancelTask { .. }
|
2022-09-14 04:38:43 +08:00
|
|
|
| DumpExport { .. }
|
|
|
|
| Snapshot => Ok(()), // There is no data associated with all these tasks
|
2022-09-07 06:22:58 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn indexes(&self) -> Option<Vec<&str>> {
|
|
|
|
use KindWithContent::*;
|
|
|
|
|
|
|
|
match self {
|
|
|
|
DumpExport { .. } | Snapshot | CancelTask { .. } => None,
|
2022-09-14 06:34:02 +08:00
|
|
|
DocumentAddition { index_uid, .. }
|
|
|
|
| DocumentUpdate { index_uid, .. }
|
2022-09-14 04:38:43 +08:00
|
|
|
| DocumentDeletion { index_uid, .. }
|
|
|
|
| DocumentClear { index_uid }
|
|
|
|
| Settings { index_uid, .. }
|
|
|
|
| IndexCreation { index_uid, .. }
|
|
|
|
| IndexUpdate { index_uid, .. }
|
|
|
|
| IndexDeletion { index_uid } => Some(vec![index_uid]),
|
|
|
|
IndexRename {
|
|
|
|
index_uid: lhs,
|
2022-09-07 06:22:58 +08:00
|
|
|
new_name: rhs,
|
|
|
|
}
|
2022-09-14 04:38:43 +08:00
|
|
|
| IndexSwap { lhs, rhs } => Some(vec![lhs, rhs]),
|
2022-09-06 22:43:59 +08:00
|
|
|
}
|
|
|
|
}
|
2022-09-07 05:49:19 +08:00
|
|
|
}
|
2022-09-06 22:43:59 +08:00
|
|
|
|
2022-09-08 02:08:07 +08:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
2022-09-07 05:49:19 +08:00
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub enum Kind {
|
2022-09-14 06:34:02 +08:00
|
|
|
DocumentAddition,
|
|
|
|
DocumentUpdate,
|
2022-09-07 05:49:19 +08:00
|
|
|
DocumentDeletion,
|
2022-09-14 04:38:43 +08:00
|
|
|
DocumentClear,
|
2022-09-07 05:49:19 +08:00
|
|
|
Settings,
|
2022-09-14 04:38:43 +08:00
|
|
|
IndexCreation,
|
|
|
|
IndexDeletion,
|
|
|
|
IndexUpdate,
|
|
|
|
IndexRename,
|
|
|
|
IndexSwap,
|
|
|
|
CancelTask,
|
|
|
|
DumpExport,
|
2022-09-07 05:49:19 +08:00
|
|
|
Snapshot,
|
2022-09-06 22:43:59 +08:00
|
|
|
}
|
2022-09-14 22:16:53 +08:00
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
#[serde(untagged)]
|
|
|
|
#[allow(clippy::large_enum_variant)]
|
|
|
|
pub enum Details {
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
DocumentAddition {
|
|
|
|
received_documents: usize,
|
|
|
|
indexed_documents: Option<u64>,
|
|
|
|
},
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
Settings {
|
|
|
|
#[serde(flatten)]
|
|
|
|
settings: Settings<Unchecked>,
|
|
|
|
},
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
IndexInfo { primary_key: Option<String> },
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
DocumentDeletion {
|
|
|
|
received_document_ids: usize,
|
|
|
|
deleted_documents: Option<u64>,
|
|
|
|
},
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
ClearAll { deleted_documents: Option<u64> },
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
Dump { dump_uid: String },
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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.
|
|
|
|
fn serialize_duration<S: Serializer>(
|
|
|
|
duration: &Option<Duration>,
|
|
|
|
serializer: S,
|
|
|
|
) -> Result<S::Ok, S::Error> {
|
|
|
|
match duration {
|
|
|
|
Some(duration) => {
|
|
|
|
// technically speaking, negative duration is not valid ISO 8601
|
|
|
|
if duration.is_negative() {
|
|
|
|
return serializer.serialize_none();
|
|
|
|
}
|
|
|
|
|
|
|
|
const SECS_PER_DAY: i64 = Duration::DAY.whole_seconds();
|
|
|
|
let secs = duration.whole_seconds();
|
|
|
|
let days = secs / SECS_PER_DAY;
|
|
|
|
let secs = secs - days * SECS_PER_DAY;
|
|
|
|
let hasdate = days != 0;
|
|
|
|
let nanos = duration.subsec_nanoseconds();
|
|
|
|
let hastime = (secs != 0 || nanos != 0) || !hasdate;
|
|
|
|
|
|
|
|
// all the following unwrap can't fail
|
|
|
|
let mut res = String::new();
|
|
|
|
write!(&mut res, "P").unwrap();
|
|
|
|
|
|
|
|
if hasdate {
|
|
|
|
write!(&mut res, "{}D", days).unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
const NANOS_PER_MILLI: i32 = Duration::MILLISECOND.subsec_nanoseconds();
|
|
|
|
const NANOS_PER_MICRO: i32 = Duration::MICROSECOND.subsec_nanoseconds();
|
|
|
|
|
|
|
|
if hastime {
|
|
|
|
if nanos == 0 {
|
|
|
|
write!(&mut res, "T{}S", secs).unwrap();
|
|
|
|
} else if nanos % NANOS_PER_MILLI == 0 {
|
|
|
|
write!(&mut res, "T{}.{:03}S", secs, nanos / NANOS_PER_MILLI).unwrap();
|
|
|
|
} else if nanos % NANOS_PER_MICRO == 0 {
|
|
|
|
write!(&mut res, "T{}.{:06}S", secs, nanos / NANOS_PER_MICRO).unwrap();
|
|
|
|
} else {
|
|
|
|
write!(&mut res, "T{}.{:09}S", secs, nanos).unwrap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
serializer.serialize_str(&res)
|
|
|
|
}
|
|
|
|
None => serializer.serialize_none(),
|
|
|
|
}
|
|
|
|
}
|