1use api::region::RegionResponse;
18use api::v1::SemanticType;
19use common_error::ext::BoxedError;
20use common_telemetry::{error, info, warn};
21use datafusion::common::HashMap;
22use mito2::engine::MITO_ENGINE_NAME;
23use snafu::{OptionExt, ResultExt};
24use store_api::region_engine::{BatchResponses, RegionEngine};
25use store_api::region_request::{AffectedRows, PathType, RegionOpenRequest, ReplayCheckpoint};
26use store_api::storage::RegionId;
27
28use crate::engine::MetricEngineInner;
29use crate::engine::create::region_options_for_metadata_region;
30use crate::engine::options::{PhysicalRegionOptions, set_data_region_options};
31use crate::error::{
32 BatchOpenMitoRegionSnafu, NoOpenRegionResultSnafu, OpenMitoRegionSnafu,
33 PhysicalRegionNotFoundSnafu, Result,
34};
35use crate::metrics::{LOGICAL_REGION_COUNT, PHYSICAL_REGION_COUNT};
36use crate::utils;
37
38impl MetricEngineInner {
39 pub async fn handle_batch_open_requests(
40 &self,
41 parallelism: usize,
42 requests: Vec<(RegionId, RegionOpenRequest)>,
43 ) -> Result<BatchResponses> {
44 let mut all_requests = Vec::with_capacity(requests.len() * 2);
46 let mut physical_region_ids = HashMap::with_capacity(requests.len());
47
48 for (region_id, request) in requests {
49 if !request.is_physical_table() {
50 warn!("Skipping non-physical table open request: {region_id}");
51 continue;
52 }
53 let physical_region_options = PhysicalRegionOptions::try_from(&request.options)?;
54 let metadata_region_id = utils::to_metadata_region_id(region_id);
55 let data_region_id = utils::to_data_region_id(region_id);
56 let (open_metadata_region_request, open_data_region_request) =
57 self.transform_open_physical_region_request(request);
58 all_requests.push((metadata_region_id, open_metadata_region_request));
59 all_requests.push((data_region_id, open_data_region_request));
60 physical_region_ids.insert(region_id, physical_region_options);
61 }
62
63 let mut results = self
64 .mito
65 .handle_batch_open_requests(parallelism, all_requests)
66 .await
67 .context(BatchOpenMitoRegionSnafu {})?
68 .into_iter()
69 .collect::<HashMap<_, _>>();
70
71 let mut responses = Vec::with_capacity(physical_region_ids.len());
72 for (physical_region_id, physical_region_options) in physical_region_ids {
73 let metadata_region_id = utils::to_metadata_region_id(physical_region_id);
74 let data_region_id = utils::to_data_region_id(physical_region_id);
75 let metadata_region_result = results.remove(&metadata_region_id);
76 let data_region_result: Option<std::result::Result<RegionResponse, BoxedError>> =
77 results.remove(&data_region_id);
78 let response = self
83 .recover_physical_region_with_results(
84 metadata_region_result,
85 data_region_result,
86 physical_region_id,
87 physical_region_options,
88 true,
89 )
90 .await
91 .map_err(BoxedError::new);
92 responses.push((physical_region_id, response));
93 }
94
95 Ok(responses)
96 }
97
98 async fn close_physical_region_on_recovery_failure(&self, physical_region_id: RegionId) {
103 info!(
104 "Closing metadata region {} and data region {} on metadata recovery failure",
105 utils::to_metadata_region_id(physical_region_id),
106 utils::to_data_region_id(physical_region_id)
107 );
108 if let Err(err) = self.close_physical_region(physical_region_id).await {
109 error!(err; "Failed to close physical region {}", physical_region_id);
110 }
111 }
112
113 pub(crate) async fn recover_physical_region_with_results(
114 &self,
115 metadata_region_result: Option<std::result::Result<RegionResponse, BoxedError>>,
116 data_region_result: Option<std::result::Result<RegionResponse, BoxedError>>,
117 physical_region_id: RegionId,
118 physical_region_options: PhysicalRegionOptions,
119 close_region_on_failure: bool,
120 ) -> Result<RegionResponse> {
121 let metadata_region_id = utils::to_metadata_region_id(physical_region_id);
122 let data_region_id = utils::to_data_region_id(physical_region_id);
123 let _ = metadata_region_result
124 .context(NoOpenRegionResultSnafu {
125 region_id: metadata_region_id,
126 })?
127 .context(OpenMitoRegionSnafu {
128 region_type: "metadata",
129 })?;
130
131 let data_region_response = data_region_result
132 .context(NoOpenRegionResultSnafu {
133 region_id: data_region_id,
134 })?
135 .context(OpenMitoRegionSnafu {
136 region_type: "data",
137 })?;
138
139 if let Err(err) = self
140 .recover_states(physical_region_id, physical_region_options)
141 .await
142 {
143 if close_region_on_failure {
144 self.close_physical_region_on_recovery_failure(physical_region_id)
145 .await;
146 }
147 return Err(err);
148 }
149 Ok(data_region_response)
150 }
151
152 pub async fn open_region(
162 &self,
163 region_id: RegionId,
164 request: RegionOpenRequest,
165 ) -> Result<AffectedRows> {
166 if request.is_physical_table() {
167 if self
168 .state
169 .read()
170 .unwrap()
171 .physical_region_states()
172 .get(®ion_id)
173 .is_some()
174 {
175 warn!(
176 "The physical region {} is already open, ignore the open request",
177 region_id
178 );
179 return Ok(0);
180 }
181 let physical_region_options = PhysicalRegionOptions::try_from(&request.options)?;
183 self.open_physical_region(region_id, request).await?;
184 if let Err(err) = self
185 .recover_states(region_id, physical_region_options)
186 .await
187 {
188 self.close_physical_region_on_recovery_failure(region_id)
189 .await;
190 return Err(err);
191 }
192
193 Ok(0)
194 } else {
195 Ok(0)
200 }
201 }
202
203 fn transform_open_physical_region_request(
209 &self,
210 request: RegionOpenRequest,
211 ) -> (RegionOpenRequest, RegionOpenRequest) {
212 let metadata_region_options = region_options_for_metadata_region(&request.options);
213 let checkpoint = request.checkpoint;
214
215 let open_metadata_region_request = RegionOpenRequest {
216 table_dir: request.table_dir.clone(),
217 path_type: PathType::Metadata,
218 options: metadata_region_options,
219 engine: MITO_ENGINE_NAME.to_string(),
220 skip_wal_replay: request.skip_wal_replay,
221 checkpoint: checkpoint.map(|checkpoint| ReplayCheckpoint {
222 entry_id: checkpoint.metadata_entry_id.unwrap_or_default(),
223 metadata_entry_id: None,
224 }),
225 requirements: request.requirements,
226 };
227
228 let mut data_region_options = request.options;
229 set_data_region_options(
230 &mut data_region_options,
231 self.config.sparse_primary_key_encoding,
232 );
233 let open_data_region_request = RegionOpenRequest {
234 table_dir: request.table_dir.clone(),
235 path_type: PathType::Data,
236 options: data_region_options,
237 engine: MITO_ENGINE_NAME.to_string(),
238 skip_wal_replay: request.skip_wal_replay,
239 checkpoint: checkpoint.map(|checkpoint| ReplayCheckpoint {
240 entry_id: checkpoint.entry_id,
241 metadata_entry_id: None,
242 }),
243 requirements: request.requirements,
244 };
245
246 (open_metadata_region_request, open_data_region_request)
247 }
248
249 async fn open_physical_region(
251 &self,
252 region_id: RegionId,
253 request: RegionOpenRequest,
254 ) -> Result<AffectedRows> {
255 let metadata_region_id = utils::to_metadata_region_id(region_id);
256 let data_region_id = utils::to_data_region_id(region_id);
257 let (open_metadata_region_request, open_data_region_request) =
258 self.transform_open_physical_region_request(request);
259 let _ = self
260 .mito
261 .handle_batch_open_requests(
262 2,
263 vec![
264 (metadata_region_id, open_metadata_region_request),
265 (data_region_id, open_data_region_request),
266 ],
267 )
268 .await
269 .context(BatchOpenMitoRegionSnafu {})?;
270
271 info!("Opened physical metric region {region_id}");
272 PHYSICAL_REGION_COUNT.inc();
273
274 Ok(0)
275 }
276
277 pub(crate) async fn recover_states(
286 &self,
287 physical_region_id: RegionId,
288 physical_region_options: PhysicalRegionOptions,
289 ) -> Result<Vec<RegionId>> {
290 let logical_regions = self
292 .metadata_region
293 .logical_regions(physical_region_id)
294 .await?;
295 common_telemetry::debug!(
296 "Recover states for physical region {}, logical regions: {:?}",
297 physical_region_id,
298 logical_regions
299 );
300 let physical_columns = self
301 .data_region
302 .physical_columns(physical_region_id)
303 .await?;
304 let primary_key_encoding = self
305 .mito
306 .get_primary_key_encoding(physical_region_id)
307 .context(PhysicalRegionNotFoundSnafu {
308 region_id: physical_region_id,
309 })?;
310
311 {
312 let mut state = self.state.write().unwrap();
313 let time_index_unit = physical_columns
317 .iter()
318 .find_map(|col| {
319 if col.semantic_type == SemanticType::Timestamp {
320 col.column_schema
321 .data_type
322 .as_timestamp()
323 .map(|data_type| data_type.unit())
324 } else {
325 None
326 }
327 })
328 .unwrap();
329 let physical_columns = physical_columns
330 .into_iter()
331 .map(|col| (col.column_schema.name.clone(), col))
332 .collect();
333 state.add_physical_region(
334 physical_region_id,
335 physical_columns,
336 primary_key_encoding,
337 physical_region_options,
338 time_index_unit,
339 );
340 for logical_region_id in &logical_regions {
342 state.add_logical_region(physical_region_id, *logical_region_id);
343 }
344 }
345
346 let mut opened_logical_region_ids = Vec::new();
347 for logical_region_id in logical_regions {
350 if self
351 .metadata_region
352 .open_logical_region(logical_region_id)
353 .await
354 {
355 opened_logical_region_ids.push(logical_region_id);
356 }
357 }
358
359 LOGICAL_REGION_COUNT.add(opened_logical_region_ids.len() as i64);
360
361 Ok(opened_logical_region_ids)
362 }
363}
364
365