Skip to content

Commit eb7bb31

Browse files
feat: delete indexer from cluster (#1284)
DELETE /cluster/{node_url} to handle deletion of all nodes including ingestor and indexer
1 parent b0c7df9 commit eb7bb31

File tree

8 files changed

+74
-55
lines changed

8 files changed

+74
-55
lines changed

src/handlers/http/cluster/mod.rs

+52-28
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pub mod utils;
2020

2121
use futures::{future, stream, StreamExt};
2222
use std::collections::HashSet;
23+
use std::sync::Arc;
2324
use std::time::Duration;
2425

2526
use actix_web::http::header::{self, HeaderMap};
@@ -31,7 +32,7 @@ use clokwerk::{AsyncScheduler, Interval};
3132
use http::{header as http_header, StatusCode};
3233
use itertools::Itertools;
3334
use relative_path::RelativePathBuf;
34-
use serde::de::Error;
35+
use serde::de::{DeserializeOwned, Error};
3536
use serde_json::error::Error as SerdeError;
3637
use serde_json::{to_vec, Value as JsonValue};
3738
use tracing::{error, info, warn};
@@ -45,7 +46,8 @@ use crate::rbac::role::model::DefaultPrivilege;
4546
use crate::rbac::user::User;
4647
use crate::stats::Stats;
4748
use crate::storage::{
48-
ObjectStorageError, ObjectStoreFormat, PARSEABLE_ROOT_DIRECTORY, STREAM_ROOT_DIRECTORY,
49+
ObjectStorage, ObjectStorageError, ObjectStoreFormat, PARSEABLE_ROOT_DIRECTORY,
50+
STREAM_ROOT_DIRECTORY,
4951
};
5052
use crate::HTTP_CLIENT;
5153

@@ -642,6 +644,9 @@ async fn fetch_nodes_info<T: Metadata>(
642644
nodes: Vec<T>,
643645
) -> Result<Vec<utils::ClusterInfo>, StreamError> {
644646
let nodes_len = nodes.len();
647+
if nodes_len == 0 {
648+
return Ok(vec![]);
649+
}
645650
let results = stream::iter(nodes)
646651
.map(|node| async move { fetch_node_info(&node).await })
647652
.buffer_unordered(nodes_len) // No concurrency limit
@@ -702,52 +707,71 @@ pub async fn get_indexer_info() -> anyhow::Result<IndexerMetadataArr> {
702707
Ok(arr)
703708
}
704709

705-
pub async fn remove_ingestor(ingestor: Path<String>) -> Result<impl Responder, PostError> {
706-
let domain_name = to_url_string(ingestor.into_inner());
710+
pub async fn remove_node(node_url: Path<String>) -> Result<impl Responder, PostError> {
711+
let domain_name = to_url_string(node_url.into_inner());
707712

708713
if check_liveness(&domain_name).await {
709714
return Err(PostError::Invalid(anyhow::anyhow!(
710-
"The ingestor is currently live and cannot be removed"
715+
"The node is currently live and cannot be removed"
711716
)));
712717
}
713718
let object_store = PARSEABLE.storage.get_object_store();
714719

715-
let ingestor_metadatas = object_store
720+
// Delete ingestor metadata
721+
let removed_ingestor =
722+
remove_node_metadata::<IngestorMetadata>(&object_store, &domain_name).await?;
723+
724+
// Delete indexer metadata
725+
let removed_indexer =
726+
remove_node_metadata::<IndexerMetadata>(&object_store, &domain_name).await?;
727+
728+
let msg = if removed_ingestor || removed_indexer {
729+
format!("node {} removed successfully", domain_name)
730+
} else {
731+
format!("node {} is not found", domain_name)
732+
};
733+
734+
info!("{}", &msg);
735+
Ok((msg, StatusCode::OK))
736+
}
737+
738+
// Helper function to remove a specific type of node metadata
739+
async fn remove_node_metadata<T: Metadata + DeserializeOwned + Default>(
740+
object_store: &Arc<dyn ObjectStorage>,
741+
domain_name: &str,
742+
) -> Result<bool, PostError> {
743+
let node_type = T::default().node_type().to_string();
744+
745+
let metadatas = object_store
716746
.get_objects(
717747
Some(&RelativePathBuf::from(PARSEABLE_ROOT_DIRECTORY)),
718-
Box::new(|file_name| file_name.starts_with("ingestor")),
748+
Box::new(move |file_name| file_name.starts_with(&node_type)),
719749
)
720750
.await?;
721751

722-
let ingestor_metadata = ingestor_metadatas
752+
let node_metadatas = metadatas
723753
.iter()
724-
.map(|elem| serde_json::from_slice::<IngestorMetadata>(elem).unwrap_or_default())
725-
.collect_vec();
754+
.filter_map(|elem| match serde_json::from_slice::<T>(elem) {
755+
Ok(meta) if meta.domain_name() == domain_name => Some(meta),
756+
_ => None,
757+
})
758+
.collect::<Vec<_>>();
726759

727-
let ingestor_metadata = ingestor_metadata
728-
.iter()
729-
.filter(|elem| elem.domain_name == domain_name)
730-
.collect_vec();
760+
if node_metadatas.is_empty() {
761+
return Ok(false);
762+
}
731763

732-
let ingestor_meta_filename = ingestor_metadata[0].file_path().to_string();
733-
let msg = match object_store
734-
.try_delete_ingestor_meta(ingestor_meta_filename)
735-
.await
736-
{
737-
Ok(_) => {
738-
format!("Ingestor {} removed successfully", domain_name)
739-
}
764+
let node_meta_filename = node_metadatas[0].file_path().to_string();
765+
match object_store.try_delete_node_meta(node_meta_filename).await {
766+
Ok(_) => Ok(true),
740767
Err(err) => {
741768
if matches!(err, ObjectStorageError::IoError(_)) {
742-
format!("Ingestor {} is not found", domain_name)
769+
Ok(false)
743770
} else {
744-
format!("Error removing ingestor {}\n Reason: {}", domain_name, err)
771+
Err(PostError::ObjectStorageError(err))
745772
}
746773
}
747-
};
748-
749-
info!("{}", &msg);
750-
Ok((msg, StatusCode::OK))
774+
}
751775
}
752776

753777
/// Fetches metrics from a node (ingestor or indexer)

src/handlers/http/modal/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,7 @@ pub trait Metadata {
553553
fn domain_name(&self) -> &str;
554554
fn token(&self) -> &str;
555555
fn node_type(&self) -> &str;
556+
fn file_path(&self) -> RelativePathBuf;
556557
}
557558

558559
impl Metadata for IngestorMetadata {
@@ -566,6 +567,9 @@ impl Metadata for IngestorMetadata {
566567
fn node_type(&self) -> &str {
567568
"ingestor"
568569
}
570+
fn file_path(&self) -> RelativePathBuf {
571+
self.file_path()
572+
}
569573
}
570574

571575
impl Metadata for IndexerMetadata {
@@ -579,6 +583,9 @@ impl Metadata for IndexerMetadata {
579583
fn node_type(&self) -> &str {
580584
"indexer"
581585
}
586+
fn file_path(&self) -> RelativePathBuf {
587+
self.file_path()
588+
}
582589
}
583590
#[cfg(test)]
584591
mod test {

src/handlers/http/modal/query_server.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -333,13 +333,13 @@ impl QueryServer {
333333
.authorize(Action::ListClusterMetrics),
334334
),
335335
)
336-
// DELETE "/cluster/{ingestor_domain:port}" ==> Delete an ingestor from the cluster
336+
// DELETE "/cluster/{node_domain:port}" ==> Delete a node from the cluster
337337
.service(
338-
web::scope("/{ingestor}").service(
338+
web::scope("/{node_url}").service(
339339
web::resource("").route(
340340
web::delete()
341-
.to(cluster::remove_ingestor)
342-
.authorize(Action::Deleteingestor),
341+
.to(cluster::remove_node)
342+
.authorize(Action::DeleteNode),
343343
),
344344
),
345345
)

src/rbac/role.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub enum Action {
5050
QueryLLM,
5151
ListCluster,
5252
ListClusterMetrics,
53-
Deleteingestor,
53+
DeleteNode,
5454
All,
5555
GetAnalytics,
5656
ListDashboard,
@@ -128,7 +128,7 @@ impl RoleBuilder {
128128
| Action::DeleteCorrelation
129129
| Action::GetCorrelation
130130
| Action::PutCorrelation
131-
| Action::Deleteingestor
131+
| Action::DeleteNode
132132
| Action::PutHotTierEnabled
133133
| Action::GetHotTierEnabled
134134
| Action::DeleteHotTierEnabled

src/storage/azure_blob.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -644,11 +644,8 @@ impl ObjectStorage for BlobStore {
644644
Ok(())
645645
}
646646

647-
async fn try_delete_ingestor_meta(
648-
&self,
649-
ingestor_filename: String,
650-
) -> Result<(), ObjectStorageError> {
651-
let file = RelativePathBuf::from(&ingestor_filename);
647+
async fn try_delete_node_meta(&self, node_filename: String) -> Result<(), ObjectStorageError> {
648+
let file = RelativePathBuf::from(&node_filename);
652649
match self.client.delete(&to_object_store_path(&file)).await {
653650
Ok(_) => Ok(()),
654651
Err(err) => {
@@ -658,7 +655,7 @@ impl ObjectStorage for BlobStore {
658655
error!("Node does not exist");
659656
Err(err.into())
660657
} else {
661-
error!("Error deleting ingestor meta file: {:?}", err);
658+
error!("Error deleting node meta file: {:?}", err);
662659
Err(err.into())
663660
}
664661
}

src/storage/localfs.rs

+2-5
Original file line numberDiff line numberDiff line change
@@ -325,11 +325,8 @@ impl ObjectStorage for LocalFS {
325325
Ok(fs::remove_dir_all(path).await?)
326326
}
327327

328-
async fn try_delete_ingestor_meta(
329-
&self,
330-
ingestor_filename: String,
331-
) -> Result<(), ObjectStorageError> {
332-
let path = self.root.join(ingestor_filename);
328+
async fn try_delete_node_meta(&self, node_filename: String) -> Result<(), ObjectStorageError> {
329+
let path = self.root.join(node_filename);
333330
Ok(fs::remove_file(path).await?)
334331
}
335332

src/storage/object_storage.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,7 @@ pub trait ObjectStorage: Debug + Send + Sync + 'static {
201201
&self,
202202
stream_name: &str,
203203
) -> Result<Vec<RelativePathBuf>, ObjectStorageError>;
204-
async fn try_delete_ingestor_meta(
205-
&self,
206-
ingestor_filename: String,
207-
) -> Result<(), ObjectStorageError>;
204+
async fn try_delete_node_meta(&self, node_filename: String) -> Result<(), ObjectStorageError>;
208205
/// Returns the amount of time taken by the `ObjectStore` to perform a get
209206
/// call.
210207
async fn get_latency(&self) -> Duration {

src/storage/s3.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -727,11 +727,8 @@ impl ObjectStorage for S3 {
727727
Ok(())
728728
}
729729

730-
async fn try_delete_ingestor_meta(
731-
&self,
732-
ingestor_filename: String,
733-
) -> Result<(), ObjectStorageError> {
734-
let file = RelativePathBuf::from(&ingestor_filename);
730+
async fn try_delete_node_meta(&self, node_filename: String) -> Result<(), ObjectStorageError> {
731+
let file = RelativePathBuf::from(&node_filename);
735732
match self.client.delete(&to_object_store_path(&file)).await {
736733
Ok(_) => Ok(()),
737734
Err(err) => {
@@ -741,7 +738,7 @@ impl ObjectStorage for S3 {
741738
error!("Node does not exist");
742739
Err(err.into())
743740
} else {
744-
error!("Error deleting ingestor meta file: {:?}", err);
741+
error!("Error deleting node meta file: {:?}", err);
745742
Err(err.into())
746743
}
747744
}

0 commit comments

Comments
 (0)