1use std::time::Instant;
16
17use common_telemetry::{error, info, warn};
18use store_api::logstore::LogStore;
19use store_api::region_request::{EnterStagingRequest, StagingPartitionDirective};
20use store_api::storage::RegionId;
21
22use crate::error::{RegionNotFoundSnafu, Result, StagingPartitionExprMismatchSnafu};
23use crate::flush::FlushReason;
24use crate::manifest::action::{RegionMetaAction, RegionMetaActionList, RegionPartitionExprChange};
25use crate::region::{MitoRegionRef, RegionLeaderState, StagingPartitionInfo};
26use crate::request::{
27 BackgroundNotify, DdlRequest, EnterStagingResult, OptionOutputTx, SenderDdlRequest,
28 WorkerRequest, WorkerRequestWithTime,
29};
30use crate::worker::RegionWorkerLoop;
31
32impl<S: LogStore> RegionWorkerLoop<S> {
33 pub(crate) async fn handle_enter_staging_request(
34 &mut self,
35 region_id: RegionId,
36 partition_directive: StagingPartitionDirective,
37 mut sender: OptionOutputTx,
38 ) {
39 let Some(region) = self.regions.writable_region_or(region_id, &mut sender) else {
40 return;
41 };
42
43 if region.is_staging() {
45 let staging_partition_info = region.manifest_ctx.staging_partition_info();
46 if staging_partition_info
48 .as_ref()
49 .map(|info| &info.partition_directive)
50 != Some(&partition_directive)
51 {
52 sender.send(Err(StagingPartitionExprMismatchSnafu {
53 manifest_expr: staging_partition_info
54 .as_ref()
55 .and_then(|info| info.partition_expr().map(ToString::to_string)),
56 request_expr: format!("{:?}", partition_directive),
57 }
58 .build()));
59 return;
60 }
61
62 sender.send(Ok(0));
64 return;
65 }
66
67 let (sender, partition_directive) = match self.compaction_scheduler.try_cancel_and_add_ddl(
69 region_id,
70 sender,
71 partition_directive,
72 |partition_directive| {
73 DdlRequest::EnterStaging(EnterStagingRequest {
74 partition_directive,
75 })
76 },
77 ) {
78 Ok(()) => {
79 self.listener.on_compaction_cancel_requested(region_id);
80 return;
81 }
82 Err(request) => request,
83 };
84
85 let version = region.version();
86 if !version.memtables.is_empty() {
87 info!("Flush region: {} before entering staging", region_id);
90 debug_assert!(!region.is_staging());
91 let task = self.new_flush_task(
92 ®ion,
93 FlushReason::EnterStaging,
94 None,
95 self.config.clone(),
96 );
97 if let Err(e) =
98 self.flush_scheduler
99 .schedule_flush(region.region_id, ®ion.version_control, task)
100 {
101 sender.send(Err(e));
103 return;
104 }
105
106 self.flush_scheduler
108 .add_ddl_request_to_pending(SenderDdlRequest {
109 region_id,
110 sender,
111 request: DdlRequest::EnterStaging(EnterStagingRequest {
112 partition_directive: partition_directive.clone(),
113 }),
114 });
115
116 return;
117 }
118
119 self.handle_enter_staging(region, partition_directive, sender);
120 }
121
122 async fn enter_staging(
123 region: &MitoRegionRef,
124 partition_directive: &StagingPartitionDirective,
125 ) -> Result<()> {
126 let now = Instant::now();
127 {
129 let mut manager = region.manifest_ctx.manifest_manager.write().await;
130 manager.wait_for_pending_checkpoint().await;
134 manager
135 .clear_staging_manifest_and_dir()
136 .await
137 .inspect_err(|e| {
138 error!(
139 e;
140 "Failed to clear staging manifest files for region {}",
141 region.region_id
142 );
143 })?;
144
145 info!(
146 "Cleared all staging manifest files for region {}, elapsed: {:?}",
147 region.region_id,
148 now.elapsed(),
149 );
150 }
151
152 let partition_expr = match partition_directive {
153 StagingPartitionDirective::UpdatePartitionExpr(partition_expr) => {
154 partition_expr.clone()
155 }
156 StagingPartitionDirective::RejectAllWrites => {
157 info!(
158 "Enter staging with reject all writes, region_id: {}",
159 region.region_id
160 );
161 return Ok(());
163 }
164 };
165
166 let change = RegionPartitionExprChange {
168 partition_expr: Some(partition_expr.clone()),
169 };
170 let action_list =
171 RegionMetaActionList::with_action(RegionMetaAction::PartitionExprChange(change));
172 region
173 .manifest_ctx
174 .update_manifest(RegionLeaderState::EnteringStaging, action_list, true)
175 .await?;
176
177 Ok(())
178 }
179
180 fn handle_enter_staging(
181 &self,
182 region: MitoRegionRef,
183 partition_directive: StagingPartitionDirective,
184 sender: OptionOutputTx,
185 ) {
186 if let Err(e) = region.set_entering_staging() {
187 sender.send(Err(e));
188 return;
189 }
190
191 let listener = self.listener.clone();
192 let request_sender = self.sender.clone();
193 common_runtime::spawn_global(async move {
194 let now = Instant::now();
195 let result = Self::enter_staging(®ion, &partition_directive).await;
196 match result {
197 Ok(_) => {
198 info!(
199 "Created staging manifest for region {}, elapsed: {:?}",
200 region.region_id,
201 now.elapsed(),
202 );
203 }
204 Err(ref e) => {
205 region
207 .manifest_ctx
208 .manifest_manager
209 .write()
210 .await
211 .unset_staging_manifest();
212 error!(
213 "Failed to create staging manifest for region {}: {:?}, elapsed: {:?}",
214 region.region_id,
215 e,
216 now.elapsed(),
217 );
218 }
219 }
220
221 let notify = WorkerRequest::Background {
222 region_id: region.region_id,
223 notify: BackgroundNotify::EnterStaging(EnterStagingResult {
224 region_id: region.region_id,
225 sender,
226 result,
227 partition_directive,
228 }),
229 };
230 listener
231 .on_enter_staging_result_begin(region.region_id)
232 .await;
233
234 if let Err(res) = request_sender
235 .send(WorkerRequestWithTime::new(notify))
236 .await
237 {
238 warn!(
239 "Failed to send enter staging result back to the worker, region_id: {}, res: {:?}",
240 region.region_id, res
241 );
242 }
243 });
244 }
245
246 pub(crate) async fn handle_enter_staging_result(
248 &mut self,
249 enter_staging_result: EnterStagingResult,
250 ) {
251 let region = match self.regions.get_region(enter_staging_result.region_id) {
252 Some(region) => region,
253 None => {
254 self.reject_region_stalled_requests(&enter_staging_result.region_id);
255 enter_staging_result.sender.send(
256 RegionNotFoundSnafu {
257 region_id: enter_staging_result.region_id,
258 }
259 .fail(),
260 );
261 return;
262 }
263 };
264
265 if enter_staging_result.result.is_ok() {
266 info!(
267 "Updating region {} staging partition directive to {:?}",
268 region.region_id, enter_staging_result.partition_directive
269 );
270 Self::update_region_staging_partition_info(
271 ®ion,
272 enter_staging_result.partition_directive,
273 );
274 region.switch_state_to_staging(RegionLeaderState::EnteringStaging);
275 } else {
276 region.switch_state_to_writable(RegionLeaderState::EnteringStaging);
277 }
278 enter_staging_result
279 .sender
280 .send(enter_staging_result.result.map(|_| 0));
281 self.handle_region_stalled_requests(&enter_staging_result.region_id, true)
283 .await;
284 }
285
286 fn update_region_staging_partition_info(
287 region: &MitoRegionRef,
288 partition_directive: StagingPartitionDirective,
289 ) {
290 region.manifest_ctx.set_staging_partition_info(
291 StagingPartitionInfo::from_partition_directive(partition_directive),
292 );
293 }
294}