mito2/worker/
handle_compaction.rs1use api::v1::region::compact_request;
16use common_telemetry::{debug, error, info};
17use store_api::logstore::LogStore;
18use store_api::region_request::RegionCompactRequest;
19use store_api::storage::RegionId;
20
21use crate::compaction::{CompactionPickFinished, CompactionTransition};
22use crate::config::IndexBuildMode;
23use crate::error::{RegionNotFoundSnafu, StaleCompactionExecutionSnafu};
24use crate::metrics::COMPACTION_REQUEST_COUNT;
25use crate::region::MitoRegionRef;
26use crate::request::{
27 BuildIndexRequest, CompactionCancelled, CompactionFailed, CompactionFinished, OnFailure,
28 OptionOutputTx,
29};
30use crate::sst::index::IndexBuildType;
31use crate::worker::RegionWorkerLoop;
32
33impl<S> RegionWorkerLoop<S> {
34 pub(crate) async fn handle_compaction_pick_finished(
35 &mut self,
36 region_id: RegionId,
37 request: CompactionPickFinished,
38 ) where
39 S: LogStore,
40 {
41 let Some(region) = self.regions.get_region(region_id) else {
42 return;
43 };
44 let transition = self
49 .compaction_scheduler
50 .handle_compaction_pick_finished(
51 request,
52 ®ion.manifest_ctx,
53 self.schema_metadata_manager.clone(),
54 )
55 .await;
56 match transition {
57 CompactionTransition::AutomaticFollowupScheduled => {
58 region.update_schedule_compaction_millis();
61 }
62 CompactionTransition::NoAction => {}
63 CompactionTransition::DdlReady(mut pending_ddls) => {
64 if !pending_ddls.is_empty() {
65 self.listener.on_compaction_result_notified(region_id).await;
68 self.handle_ddl_requests(&mut pending_ddls).await;
69 }
70 }
71 }
72 }
73
74 pub(crate) async fn handle_compaction_request(
76 &mut self,
77 region_id: RegionId,
78 req: RegionCompactRequest,
79 mut sender: OptionOutputTx,
80 ) {
81 let Some(region) = self.regions.writable_region_or(region_id, &mut sender) else {
82 return;
83 };
84 COMPACTION_REQUEST_COUNT.inc();
85 let parallelism = req.parallelism.unwrap_or(1) as usize;
86 match self.compaction_scheduler.schedule_manual_compaction(
87 req.options,
88 ®ion.version_control,
89 ®ion.access_layer,
90 sender,
91 ®ion.manifest_ctx,
92 self.schema_metadata_manager.clone(),
93 parallelism,
94 req.time_range,
95 ) {
96 Ok(true) => info!(
100 "Successfully scheduled compaction task for region: {}",
101 region_id
102 ),
103 Ok(false) => {}
104 Err(e) => {
105 error!(e; "Failed to schedule compaction task for region: {}", region_id);
106 }
107 }
108 }
109
110 pub(crate) async fn handle_compaction_finished(
112 &mut self,
113 region_id: RegionId,
114 mut request: CompactionFinished,
115 ) where
116 S: LogStore,
117 {
118 let region = match self.regions.get_region(region_id) {
119 Some(region) => region,
120 None => {
121 request.on_failure(RegionNotFoundSnafu { region_id }.build());
122 return;
123 }
124 };
125 if !self
127 .compaction_scheduler
128 .is_current_execution(region_id, &request.execution)
129 {
130 request.on_failure(StaleCompactionExecutionSnafu { region_id }.build());
131 return;
132 }
133 let execution = request.execution.clone();
134 let made_progress = request.edit.files_to_remove.len() > request.edit.files_to_add.len();
139
140 region.version_control.apply_edit(
141 Some(request.edit.clone()),
142 &[],
143 region.file_purger.clone(),
144 );
145
146 let index_build_file_metas = std::mem::take(&mut request.edit.files_to_add);
147
148 request.on_success();
150 self.listener.on_compaction_result_notified(region_id).await;
151
152 if self.config.index.build_mode == IndexBuildMode::Async
154 && !index_build_file_metas.is_empty()
155 {
156 self.handle_rebuild_index(
157 BuildIndexRequest {
158 region_id,
159 build_type: IndexBuildType::Compact,
160 file_metas: index_build_file_metas,
161 },
162 OptionOutputTx::new(None),
163 )
164 .await;
165 }
166
167 let transition = self
169 .compaction_scheduler
170 .on_execution_finished(
171 region_id,
172 &execution,
173 ®ion.manifest_ctx,
174 self.schema_metadata_manager.clone(),
175 made_progress,
176 )
177 .await;
178 match transition {
179 CompactionTransition::AutomaticFollowupScheduled => {
180 region.update_schedule_compaction_millis();
181 }
182 CompactionTransition::NoAction => {}
183 CompactionTransition::DdlReady(mut pending_ddls) => {
184 self.handle_ddl_requests(&mut pending_ddls).await;
185 }
186 }
187 }
188
189 pub(crate) async fn handle_compaction_cancelled(
190 &mut self,
191 region_id: RegionId,
192 request: CompactionCancelled,
193 ) where
194 S: LogStore,
195 {
196 let execution = request.execution.clone();
197 let is_current = self.regions.get_region(region_id).is_some_and(|_| {
198 self.compaction_scheduler
199 .is_current_execution(region_id, &execution)
200 });
201 request.on_success();
202
203 if !is_current {
204 return;
205 }
206
207 let mut pending_ddls = self
209 .compaction_scheduler
210 .on_execution_cancelled(region_id, &execution)
211 .await;
212 if !pending_ddls.is_empty() {
213 self.listener.on_compaction_result_notified(region_id).await;
214 }
215
216 self.handle_ddl_requests(&mut pending_ddls).await;
217 }
218
219 pub(crate) async fn handle_compaction_failure(&mut self, req: CompactionFailed) {
221 if self.regions.get_region(req.region_id).is_none() {
222 return;
223 }
224 if !self
225 .compaction_scheduler
226 .is_current_execution(req.region_id, &req.execution)
227 {
228 debug!(
229 "Ignores stale compaction failure for region {}: {:?}",
230 req.region_id, req.err
231 );
232 return;
233 }
234
235 error!(req.err; "Failed to compact region: {}", req.region_id);
236 self.compaction_scheduler
237 .on_execution_failed(req.region_id, &req.execution, req.err);
238 }
239
240 pub(crate) async fn schedule_compaction(&mut self, region: &MitoRegionRef) {
242 if region.is_staging() || region.is_enter_staging() {
243 info!(
244 "Region {} is staging or entering staging, skip compaction",
245 region.region_id
246 );
247 return;
248 }
249 let now = self.time_provider.current_time_millis();
250 if now - region.last_schedule_compaction_millis()
251 >= self.config.min_compaction_interval.as_millis() as i64
252 {
253 debug!(
254 "minimal compaction interval time {:?} has passed, scheduling next compaction",
255 self.config.min_compaction_interval
256 );
257 match self.compaction_scheduler.schedule_automatic_compaction(
258 compact_request::Options::Regular(Default::default()),
259 ®ion.version_control,
260 ®ion.access_layer,
261 ®ion.manifest_ctx,
262 self.schema_metadata_manager.clone(),
263 ) {
264 Ok(true) => region.update_schedule_compaction_millis(),
265 Ok(false) => {}
266 Err(e) => {
267 error!(e; "Failed to schedule compaction for region: {}", region.region_id)
268 }
269 }
270 }
271 }
272}