mito2/series_index/
purger.rs1use std::fmt::{self, Debug, Formatter};
18
19use common_telemetry::{info, warn};
20use object_store::{ErrorKind, ObjectStore};
21use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender, unbounded_channel};
22
23use crate::metrics::SERIES_INDEX_FILE_OPERATION_TOTAL;
24use crate::series_index::catalog::series_index_path;
25use crate::sst::file::RegionFileId;
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28pub(crate) enum IndexFileType {
29 Range,
30 Series,
31}
32
33impl IndexFileType {
34 fn as_str(self) -> &'static str {
35 match self {
36 Self::Range => "range",
37 Self::Series => "series",
38 }
39 }
40}
41
42#[derive(Debug, Clone, Copy)]
43pub(crate) struct PurgeRequest {
44 pub(crate) file_id: RegionFileId,
45}
46
47#[derive(Clone)]
48pub(crate) struct IndexFilePurger {
49 store: ObjectStore,
50 sender: UnboundedSender<PurgeRequest>,
51}
52
53impl Debug for IndexFilePurger {
54 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
55 f.debug_struct("IndexFilePurger").finish_non_exhaustive()
56 }
57}
58
59impl IndexFilePurger {
60 pub(crate) fn purge(&self, request: PurgeRequest) {
61 if let Err(error) = self.sender.send(request) {
62 let store = self.store.clone();
63 common_runtime::spawn_global(async move {
64 purge_file(&store, error.0).await;
65 });
66 }
67 }
68}
69
70pub(crate) fn file_operation(index_type: IndexFileType, operation: &str, result: &str) {
71 SERIES_INDEX_FILE_OPERATION_TOTAL
72 .with_label_values(&[index_type.as_str(), operation, result])
73 .inc();
74}
75
76pub(crate) async fn run_index_purge_task(
78 worker_id: u32,
79 store: ObjectStore,
80 mut receiver: UnboundedReceiver<PurgeRequest>,
81) {
82 info!("Start series-index purge task, worker: {worker_id}");
83 while let Some(request) = receiver.recv().await {
84 purge_file(&store, request).await;
85 }
86 info!("Stop series-index purge task, worker: {worker_id}");
87}
88
89async fn purge_file(store: &ObjectStore, request: PurgeRequest) {
90 let path = series_index_path(request.file_id.region_id(), request.file_id.file_id());
91 match store.delete(&path).await {
92 Ok(()) => {
93 file_operation(IndexFileType::Series, "delete", "success");
94 }
95 Err(error) if error.kind() == ErrorKind::NotFound => {
96 file_operation(IndexFileType::Series, "delete", "success");
97 }
98 Err(error) => {
99 file_operation(IndexFileType::Series, "delete", "failure");
100 warn!(error; "Failed to delete series index, index_type: {}, path: {}, phase: deletion", IndexFileType::Series.as_str(), path);
101 }
102 }
103}
104
105pub(crate) fn series_index_channel(
106 store: ObjectStore,
107) -> (IndexFilePurger, UnboundedReceiver<PurgeRequest>) {
108 let (sender, receiver) = unbounded_channel();
109 (IndexFilePurger { store, sender }, receiver)
110}