Add index rename feature

This commit is contained in:
Quentin de Quelen
2025-08-01 23:17:19 +02:00
committed by Tamo
parent 3240d89e81
commit 0f1c78b185
6 changed files with 100 additions and 4 deletions

View File

@ -526,6 +526,20 @@ impl IndexMapper {
Ok(()) Ok(())
} }
/// Rename an index.
pub fn rename(&self, wtxn: &mut RwTxn, current: &str, new: &str) -> Result<()> {
let uuid = self
.index_mapping
.get(wtxn, current)?
.ok_or_else(|| Error::IndexNotFound(current.to_string()))?;
if self.index_mapping.get(wtxn, new)?.is_some() {
return Err(Error::IndexAlreadyExists(new.to_string()));
}
self.index_mapping.delete(wtxn, current)?;
self.index_mapping.put(wtxn, new, &uuid)?;
Ok(())
}
/// The stats of an index. /// The stats of an index.
/// ///
/// If available in the cache, they are directly returned. /// If available in the cache, they are directly returned.

View File

@ -125,6 +125,12 @@ make_enum_progress! {
} }
} }
make_enum_progress! {
pub enum RenameIndexProgress {
RenamingTheIndex,
}
}
make_enum_progress! { make_enum_progress! {
pub enum DeleteIndexProgress { pub enum DeleteIndexProgress {
DeletingTheIndex, DeletingTheIndex,

View File

@ -24,6 +24,7 @@ enum AutobatchKind {
IndexCreation, IndexCreation,
IndexDeletion, IndexDeletion,
IndexUpdate, IndexUpdate,
IndexRename,
IndexSwap, IndexSwap,
} }
@ -67,6 +68,7 @@ impl From<KindWithContent> for AutobatchKind {
KindWithContent::IndexDeletion { .. } => AutobatchKind::IndexDeletion, KindWithContent::IndexDeletion { .. } => AutobatchKind::IndexDeletion,
KindWithContent::IndexCreation { .. } => AutobatchKind::IndexCreation, KindWithContent::IndexCreation { .. } => AutobatchKind::IndexCreation,
KindWithContent::IndexUpdate { .. } => AutobatchKind::IndexUpdate, KindWithContent::IndexUpdate { .. } => AutobatchKind::IndexUpdate,
KindWithContent::IndexRename { .. } => AutobatchKind::IndexRename,
KindWithContent::IndexSwap { .. } => AutobatchKind::IndexSwap, KindWithContent::IndexSwap { .. } => AutobatchKind::IndexSwap,
KindWithContent::TaskCancelation { .. } KindWithContent::TaskCancelation { .. }
| KindWithContent::TaskDeletion { .. } | KindWithContent::TaskDeletion { .. }
@ -115,6 +117,9 @@ pub enum BatchKind {
IndexUpdate { IndexUpdate {
id: TaskId, id: TaskId,
}, },
IndexRename {
id: TaskId,
},
IndexSwap { IndexSwap {
id: TaskId, id: TaskId,
}, },
@ -176,6 +181,13 @@ impl BatchKind {
)), )),
false, false,
), ),
K::IndexRename => (
Break((
BatchKind::IndexRename { id: task_id },
BatchStopReason::TaskCannotBeBatched { kind, id: task_id },
)),
false,
),
K::IndexSwap => ( K::IndexSwap => (
Break(( Break((
BatchKind::IndexSwap { id: task_id }, BatchKind::IndexSwap { id: task_id },
@ -288,7 +300,7 @@ impl BatchKind {
match (self, autobatch_kind) { match (self, autobatch_kind) {
// We don't batch any of these operations // We don't batch any of these operations
(this, K::IndexCreation | K::IndexUpdate | K::IndexSwap | K::DocumentEdition) => Break((this, BatchStopReason::TaskCannotBeBatched { kind, id })), (this, K::IndexCreation | K::IndexUpdate | K::IndexRename | 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. // 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) => { (this, kind) if !index_already_exists && this.allow_index_creation() == Some(false) && kind.allow_index_creation() == Some(true) => {
Break((this, BatchStopReason::IndexCreationMismatch { id })) Break((this, BatchStopReason::IndexCreationMismatch { id }))

View File

@ -40,6 +40,11 @@ pub(crate) enum Batch {
primary_key: Option<String>, primary_key: Option<String>,
task: Task, task: Task,
}, },
IndexRename {
index_uid: String,
new_index_uid: String,
task: Task,
},
IndexDeletion { IndexDeletion {
index_uid: String, index_uid: String,
tasks: Vec<Task>, tasks: Vec<Task>,
@ -108,7 +113,8 @@ impl Batch {
| Batch::Dump(task) | Batch::Dump(task)
| Batch::IndexCreation { task, .. } | Batch::IndexCreation { task, .. }
| Batch::Export { task } | Batch::Export { task }
| Batch::IndexUpdate { task, .. } => { | Batch::IndexUpdate { task, .. }
| Batch::IndexRename { task, .. } => {
RoaringBitmap::from_sorted_iter(std::iter::once(task.uid)).unwrap() RoaringBitmap::from_sorted_iter(std::iter::once(task.uid)).unwrap()
} }
Batch::SnapshotCreation(tasks) Batch::SnapshotCreation(tasks)
@ -153,6 +159,7 @@ impl Batch {
IndexOperation { op, .. } => Some(op.index_uid()), IndexOperation { op, .. } => Some(op.index_uid()),
IndexCreation { index_uid, .. } IndexCreation { index_uid, .. }
| IndexUpdate { index_uid, .. } | IndexUpdate { index_uid, .. }
| IndexRename { index_uid, .. }
| IndexDeletion { index_uid, .. } => Some(index_uid), | IndexDeletion { index_uid, .. } => Some(index_uid),
} }
} }
@ -171,6 +178,7 @@ impl fmt::Display for Batch {
Batch::IndexOperation { op, .. } => write!(f, "{op}")?, Batch::IndexOperation { op, .. } => write!(f, "{op}")?,
Batch::IndexCreation { .. } => f.write_str("IndexCreation")?, Batch::IndexCreation { .. } => f.write_str("IndexCreation")?,
Batch::IndexUpdate { .. } => f.write_str("IndexUpdate")?, Batch::IndexUpdate { .. } => f.write_str("IndexUpdate")?,
Batch::IndexRename { .. } => f.write_str("IndexRename")?,
Batch::IndexDeletion { .. } => f.write_str("IndexDeletion")?, Batch::IndexDeletion { .. } => f.write_str("IndexDeletion")?,
Batch::IndexSwap { .. } => f.write_str("IndexSwap")?, Batch::IndexSwap { .. } => f.write_str("IndexSwap")?,
Batch::Export { .. } => f.write_str("Export")?, Batch::Export { .. } => f.write_str("Export")?,
@ -411,6 +419,16 @@ impl IndexScheduler {
}; };
Ok(Some(Batch::IndexUpdate { index_uid, primary_key, task })) Ok(Some(Batch::IndexUpdate { index_uid, primary_key, task }))
} }
BatchKind::IndexRename { id } => {
let mut task =
self.queue.tasks.get_task(rtxn, id)?.ok_or(Error::CorruptedTaskQueue)?;
current_batch.processing(Some(&mut task));
let (new_uid) = match &task.kind {
KindWithContent::IndexRename { new_index_uid, .. } => new_index_uid.clone(),
_ => unreachable!(),
};
Ok(Some(Batch::IndexRename { index_uid, new_index_uid: new_uid, task }))
}
BatchKind::IndexDeletion { ids } => Ok(Some(Batch::IndexDeletion { BatchKind::IndexDeletion { ids } => Ok(Some(Batch::IndexDeletion {
index_uid, index_uid,
index_has_been_created: must_create_index, index_has_been_created: must_create_index,

View File

@ -15,7 +15,7 @@ use super::create_batch::Batch;
use crate::processing::{ use crate::processing::{
AtomicBatchStep, AtomicTaskStep, CreateIndexProgress, DeleteIndexProgress, FinalizingIndexStep, AtomicBatchStep, AtomicTaskStep, CreateIndexProgress, DeleteIndexProgress, FinalizingIndexStep,
InnerSwappingTwoIndexes, SwappingTheIndexes, TaskCancelationProgress, TaskDeletionProgress, InnerSwappingTwoIndexes, SwappingTheIndexes, TaskCancelationProgress, TaskDeletionProgress,
UpdateIndexProgress, UpdateIndexProgress, RenameIndexProgress,
}; };
use crate::utils::{ use crate::utils::{
self, remove_n_tasks_datetime_earlier_than, remove_task_datetime, swap_index_uid_in_task, self, remove_n_tasks_datetime_earlier_than, remove_task_datetime, swap_index_uid_in_task,
@ -229,6 +229,20 @@ impl IndexScheduler {
progress, progress,
) )
} }
Batch::IndexRename { index_uid, new_index_uid, mut task } => {
progress.update_progress(RenameIndexProgress::RenamingTheIndex);
let mut wtxn = self.env.write_txn()?;
self.index_mapper.rename(&mut wtxn, &index_uid, &new_index_uid)?;
self.queue.tasks.update_index(&mut wtxn, &new_index_uid, |bm| {
let old = self.queue.tasks.index_tasks(&wtxn, &index_uid).unwrap_or_default();
*bm |= &old;
})?;
self.queue.tasks.update_index(&mut wtxn, &index_uid, |bm| bm.clear())?;
wtxn.commit()?;
task.status = Status::Succeeded;
task.details = Some(Details::IndexRename(IndexRenameDetails { old_uid: index_uid, new_uid: new_index_uid }));
Ok((vec![task], ProcessBatchInfo::default()))
}
Batch::IndexUpdate { index_uid, primary_key, mut task } => { Batch::IndexUpdate { index_uid, primary_key, mut task } => {
progress.update_progress(UpdateIndexProgress::UpdatingTheIndex); progress.update_progress(UpdateIndexProgress::UpdatingTheIndex);
let rtxn = self.env.read_txn()?; let rtxn = self.env.read_txn()?;

View File

@ -141,6 +141,10 @@ pub enum KindWithContent {
index_uid: String, index_uid: String,
primary_key: Option<String>, primary_key: Option<String>,
}, },
IndexRename {
index_uid: String,
new_index_uid: String,
},
IndexSwap { IndexSwap {
swaps: Vec<IndexSwap>, swaps: Vec<IndexSwap>,
}, },
@ -174,6 +178,13 @@ pub struct IndexSwap {
pub indexes: (String, String), pub indexes: (String, String),
} }
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct IndexRenameDetails {
pub old_uid: String,
pub new_uid: String,
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)] #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct ExportIndexSettings { pub struct ExportIndexSettings {
@ -193,6 +204,7 @@ impl KindWithContent {
KindWithContent::IndexCreation { .. } => Kind::IndexCreation, KindWithContent::IndexCreation { .. } => Kind::IndexCreation,
KindWithContent::IndexDeletion { .. } => Kind::IndexDeletion, KindWithContent::IndexDeletion { .. } => Kind::IndexDeletion,
KindWithContent::IndexUpdate { .. } => Kind::IndexUpdate, KindWithContent::IndexUpdate { .. } => Kind::IndexUpdate,
KindWithContent::IndexRename { .. } => Kind::IndexRename,
KindWithContent::IndexSwap { .. } => Kind::IndexSwap, KindWithContent::IndexSwap { .. } => Kind::IndexSwap,
KindWithContent::TaskCancelation { .. } => Kind::TaskCancelation, KindWithContent::TaskCancelation { .. } => Kind::TaskCancelation,
KindWithContent::TaskDeletion { .. } => Kind::TaskDeletion, KindWithContent::TaskDeletion { .. } => Kind::TaskDeletion,
@ -222,6 +234,7 @@ impl KindWithContent {
| IndexCreation { index_uid, .. } | IndexCreation { index_uid, .. }
| IndexUpdate { index_uid, .. } | IndexUpdate { index_uid, .. }
| IndexDeletion { index_uid } => vec![index_uid], | IndexDeletion { index_uid } => vec![index_uid],
IndexRename { index_uid, new_index_uid } => vec![index_uid, new_index_uid],
IndexSwap { swaps } => { IndexSwap { swaps } => {
let mut indexes = HashSet::<&str>::default(); let mut indexes = HashSet::<&str>::default();
for swap in swaps { for swap in swaps {
@ -274,6 +287,12 @@ impl KindWithContent {
| KindWithContent::IndexUpdate { primary_key, .. } => { | KindWithContent::IndexUpdate { primary_key, .. } => {
Some(Details::IndexInfo { primary_key: primary_key.clone() }) Some(Details::IndexInfo { primary_key: primary_key.clone() })
} }
KindWithContent::IndexRename { index_uid, new_index_uid } => {
Some(Details::IndexRename {
old_uid: index_uid.clone(),
new_uid: new_index_uid.clone(),
})
}
KindWithContent::IndexSwap { swaps } => { KindWithContent::IndexSwap { swaps } => {
Some(Details::IndexSwap { swaps: swaps.clone() }) Some(Details::IndexSwap { swaps: swaps.clone() })
} }
@ -344,6 +363,12 @@ impl KindWithContent {
Some(Details::SettingsUpdate { settings: new_settings.clone() }) Some(Details::SettingsUpdate { settings: new_settings.clone() })
} }
KindWithContent::IndexDeletion { .. } => None, KindWithContent::IndexDeletion { .. } => None,
KindWithContent::IndexRename { index_uid, new_index_uid } => {
Some(Details::IndexRename {
old_uid: index_uid.clone(),
new_uid: new_index_uid.clone(),
})
}
KindWithContent::IndexCreation { primary_key, .. } KindWithContent::IndexCreation { primary_key, .. }
| KindWithContent::IndexUpdate { primary_key, .. } => { | KindWithContent::IndexUpdate { primary_key, .. } => {
Some(Details::IndexInfo { primary_key: primary_key.clone() }) Some(Details::IndexInfo { primary_key: primary_key.clone() })
@ -538,6 +563,7 @@ pub enum Kind {
IndexCreation, IndexCreation,
IndexDeletion, IndexDeletion,
IndexUpdate, IndexUpdate,
IndexRename,
IndexSwap, IndexSwap,
TaskCancelation, TaskCancelation,
TaskDeletion, TaskDeletion,
@ -556,7 +582,8 @@ impl Kind {
| Kind::SettingsUpdate | Kind::SettingsUpdate
| Kind::IndexCreation | Kind::IndexCreation
| Kind::IndexDeletion | Kind::IndexDeletion
| Kind::IndexUpdate => true, | Kind::IndexUpdate
| Kind::IndexRename => true,
Kind::IndexSwap Kind::IndexSwap
| Kind::TaskCancelation | Kind::TaskCancelation
| Kind::TaskDeletion | Kind::TaskDeletion
@ -577,6 +604,7 @@ impl Display for Kind {
Kind::IndexCreation => write!(f, "indexCreation"), Kind::IndexCreation => write!(f, "indexCreation"),
Kind::IndexDeletion => write!(f, "indexDeletion"), Kind::IndexDeletion => write!(f, "indexDeletion"),
Kind::IndexUpdate => write!(f, "indexUpdate"), Kind::IndexUpdate => write!(f, "indexUpdate"),
Kind::IndexRename => write!(f, "indexRename"),
Kind::IndexSwap => write!(f, "indexSwap"), Kind::IndexSwap => write!(f, "indexSwap"),
Kind::TaskCancelation => write!(f, "taskCancelation"), Kind::TaskCancelation => write!(f, "taskCancelation"),
Kind::TaskDeletion => write!(f, "taskDeletion"), Kind::TaskDeletion => write!(f, "taskDeletion"),
@ -595,6 +623,8 @@ impl FromStr for Kind {
Ok(Kind::IndexCreation) Ok(Kind::IndexCreation)
} else if kind.eq_ignore_ascii_case("indexUpdate") { } else if kind.eq_ignore_ascii_case("indexUpdate") {
Ok(Kind::IndexUpdate) Ok(Kind::IndexUpdate)
} else if kind.eq_ignore_ascii_case("indexRename") {
Ok(Kind::IndexRename)
} else if kind.eq_ignore_ascii_case("indexSwap") { } else if kind.eq_ignore_ascii_case("indexSwap") {
Ok(Kind::IndexSwap) Ok(Kind::IndexSwap)
} else if kind.eq_ignore_ascii_case("indexDeletion") { } else if kind.eq_ignore_ascii_case("indexDeletion") {
@ -692,6 +722,7 @@ pub enum Details {
IndexSwap { IndexSwap {
swaps: Vec<IndexSwap>, swaps: Vec<IndexSwap>,
}, },
IndexRename(IndexRenameDetails),
Export { Export {
url: String, url: String,
api_key: Option<String>, api_key: Option<String>,
@ -737,6 +768,7 @@ impl Details {
Self::SettingsUpdate { .. } Self::SettingsUpdate { .. }
| Self::IndexInfo { .. } | Self::IndexInfo { .. }
| Self::Dump { .. } | Self::Dump { .. }
| Self::IndexRename { .. }
| Self::Export { .. } | Self::Export { .. }
| Self::UpgradeDatabase { .. } | Self::UpgradeDatabase { .. }
| Self::IndexSwap { .. } => (), | Self::IndexSwap { .. } => (),