metric_engine/engine/sync/
region.rs1use std::time::Instant;
16
17use common_error::ext::BoxedError;
18use common_telemetry::info;
19use mito2::manifest::action::RegionEdit;
20use snafu::{OptionExt, ResultExt, ensure};
21use store_api::region_engine::{MitoCopyRegionFromRequest, SyncRegionFromResponse};
22use store_api::storage::RegionId;
23
24use crate::engine::MetricEngineInner;
25use crate::error::{
26 MissingFilesSnafu, MitoCopyRegionFromOperationSnafu, MitoEditRegionSnafu,
27 PhysicalRegionNotFoundSnafu, Result,
28};
29use crate::utils;
30
31impl MetricEngineInner {
32 pub(crate) async fn sync_region_from_region(
42 &self,
43 region_id: RegionId,
44 source_region_id: RegionId,
45 parallelism: usize,
46 ) -> Result<SyncRegionFromResponse> {
47 let source_metadata_region_id = utils::to_metadata_region_id(source_region_id);
48 let target_metadata_region_id = utils::to_metadata_region_id(region_id);
49 let target_data_region_id = utils::to_data_region_id(region_id);
50 let source_data_region_id = utils::to_data_region_id(source_region_id);
51 info!(
52 "Syncing region from region {} to region {}, parallelism: {}",
53 source_region_id, region_id, parallelism
54 );
55
56 let res = self
57 .mito
58 .copy_region_from(
59 target_metadata_region_id,
60 MitoCopyRegionFromRequest {
61 source_region_id: source_metadata_region_id,
62 parallelism,
63 },
64 )
65 .await
66 .map_err(BoxedError::new)
67 .context(MitoCopyRegionFromOperationSnafu {
68 source_region_id: source_metadata_region_id,
69 target_region_id: target_metadata_region_id,
70 })?;
71
72 if res.copied_file_ids.is_empty() {
73 info!(
74 "No files were copied from source region {} to target region {}, copied file ids are empty",
75 source_metadata_region_id, target_metadata_region_id
76 );
77 return Ok(SyncRegionFromResponse::Metric {
78 metadata_synced: false,
79 data_synced: false,
80 new_opened_logical_region_ids: vec![],
81 });
82 }
83
84 let target_region = self.mito.find_region(target_metadata_region_id).context(
85 PhysicalRegionNotFoundSnafu {
86 region_id: target_metadata_region_id,
87 },
88 )?;
89 let files_to_remove = target_region.file_metas(&res.copied_file_ids).await;
90 let missing_file_ids = res
91 .copied_file_ids
92 .iter()
93 .zip(&files_to_remove)
94 .filter_map(|(file_id, maybe_meta)| {
95 if maybe_meta.is_none() {
96 Some(*file_id)
97 } else {
98 None
99 }
100 })
101 .collect::<Vec<_>>();
102 ensure!(
105 missing_file_ids.is_empty(),
106 MissingFilesSnafu {
107 region_id: target_metadata_region_id,
108 file_ids: missing_file_ids,
109 }
110 );
111 let files_to_remove = files_to_remove.into_iter().flatten().collect::<Vec<_>>();
112 self.metadata_region
114 .transform_logical_region_metadata(target_data_region_id, source_data_region_id)
115 .await?;
116
117 let edit = RegionEdit {
118 files_to_add: vec![],
119 files_to_remove: files_to_remove.clone(),
120 timestamp_ms: Some(chrono::Utc::now().timestamp_millis()),
121 compaction_time_window: None,
122 flushed_entry_id: None,
123 flushed_sequence: None,
124 committed_sequence: None,
125 wal_recovery: Default::default(),
126 };
127 self.mito
128 .edit_region(target_metadata_region_id, edit)
129 .await
130 .map_err(BoxedError::new)
131 .context(MitoEditRegionSnafu {
132 region_id: target_metadata_region_id,
133 })?;
134 info!(
135 "Successfully edit metadata region: {} after syncing from source metadata region: {}, files to remove: {:?}",
136 target_metadata_region_id,
137 source_metadata_region_id,
138 files_to_remove
139 .iter()
140 .map(|meta| meta.file_id)
141 .collect::<Vec<_>>(),
142 );
143
144 let now = Instant::now();
145 let physical_region_options = *self
148 .state
149 .read()
150 .unwrap()
151 .physical_region_states()
152 .get(&target_data_region_id)
153 .context(PhysicalRegionNotFoundSnafu {
154 region_id: target_data_region_id,
155 })?
156 .options();
157 let new_opened_logical_region_ids = self
158 .recover_states(target_data_region_id, physical_region_options)
159 .await?;
160 info!(
161 "Sync metadata region from source region {} to target region {}, recover states cost: {:?}, new opened logical region ids: {:?}",
162 source_metadata_region_id,
163 target_metadata_region_id,
164 now.elapsed(),
165 new_opened_logical_region_ids
166 );
167
168 Ok(SyncRegionFromResponse::Metric {
169 metadata_synced: true,
170 data_synced: false,
171 new_opened_logical_region_ids,
172 })
173 }
174}
175
176#[cfg(test)]
177mod tests {
178
179 use common_error::ext::ErrorExt;
180 use common_error::status_code::StatusCode;
181 use common_telemetry::debug;
182 use store_api::metric_engine_consts::{METRIC_ENGINE_NAME, PHYSICAL_TABLE_METADATA_KEY};
183 use store_api::region_engine::{RegionEngine, SyncRegionFromRequest};
184 use store_api::region_request::{
185 BatchRegionDdlRequest, PathType, RegionCloseRequest, RegionFlushRequest, RegionOpenRequest,
186 RegionRequest,
187 };
188 use store_api::storage::RegionId;
189
190 use crate::metadata_region::MetadataRegion;
191 use crate::test_util::{TestEnv, create_logical_region_request};
192
193 async fn assert_logical_table_columns(
194 metadata_region: &MetadataRegion,
195 physical_region_id: RegionId,
196 logical_region_id: RegionId,
197 expected_columns: &[&str],
198 ) {
199 let mut columns = metadata_region
200 .logical_columns(physical_region_id, logical_region_id)
201 .await
202 .unwrap()
203 .into_iter()
204 .map(|(n, _)| n)
205 .collect::<Vec<_>>();
206 columns.sort_unstable();
207 assert_eq!(columns, expected_columns);
208 }
209
210 #[tokio::test]
211 async fn test_sync_region_from_region() {
212 common_telemetry::init_default_ut_logging();
213 let env = TestEnv::new().await;
214 let metric_engine = env.metric();
215 let source_physical_region_id = RegionId::new(1024, 0);
216 let logical_region_id1 = RegionId::new(1025, 0);
217 let logical_region_id2 = RegionId::new(1026, 0);
218 env.create_physical_region(source_physical_region_id, "/test_dir1", vec![])
219 .await;
220 let region_create_request1 =
221 create_logical_region_request(&["job"], source_physical_region_id, "logical1");
222 let region_create_request2 =
223 create_logical_region_request(&["host"], source_physical_region_id, "logical2");
224 metric_engine
225 .handle_batch_ddl_requests(BatchRegionDdlRequest::Create(vec![
226 (logical_region_id1, region_create_request1),
227 (logical_region_id2, region_create_request2),
228 ]))
229 .await
230 .unwrap();
231 debug!("Flushing source physical region");
232 metric_engine
233 .handle_request(
234 source_physical_region_id,
235 RegionRequest::Flush(RegionFlushRequest::default()),
236 )
237 .await
238 .unwrap();
239 let logical_regions = metric_engine
240 .logical_regions(source_physical_region_id)
241 .await
242 .unwrap();
243 assert!(logical_regions.contains(&logical_region_id1));
244 assert!(logical_regions.contains(&logical_region_id2));
245
246 let target_physical_region_id = RegionId::new(1024, 1);
247 let target_logical_region_id1 = RegionId::new(1025, 1);
248 let target_logical_region_id2 = RegionId::new(1026, 1);
249 env.create_physical_region(target_physical_region_id, "/test_dir1", vec![])
251 .await;
252 let r = metric_engine
253 .sync_region(
254 target_physical_region_id,
255 SyncRegionFromRequest::FromRegion {
256 source_region_id: source_physical_region_id,
257 parallelism: 1,
258 },
259 )
260 .await
261 .unwrap();
262 let new_opened_logical_region_ids = r.new_opened_logical_region_ids().unwrap();
263 assert_eq!(new_opened_logical_region_ids.len(), 2);
264 assert!(new_opened_logical_region_ids.contains(&target_logical_region_id1));
265 assert!(new_opened_logical_region_ids.contains(&target_logical_region_id2));
266 debug!("Sync region from again");
267 assert_logical_table_columns(
268 &env.metadata_region(),
269 target_physical_region_id,
270 target_logical_region_id1,
271 &["greptime_timestamp", "greptime_value", "job"],
272 )
273 .await;
274 assert_logical_table_columns(
275 &env.metadata_region(),
276 target_physical_region_id,
277 target_logical_region_id2,
278 &["greptime_timestamp", "greptime_value", "host"],
279 )
280 .await;
281 let logical_regions = env
282 .metadata_region()
283 .logical_regions(target_physical_region_id)
284 .await
285 .unwrap();
286 assert_eq!(logical_regions.len(), 2);
287 assert!(logical_regions.contains(&target_logical_region_id1));
288 assert!(logical_regions.contains(&target_logical_region_id2));
289
290 let r = metric_engine
292 .sync_region(
293 target_physical_region_id,
294 SyncRegionFromRequest::FromRegion {
295 source_region_id: source_physical_region_id,
296 parallelism: 1,
297 },
298 )
299 .await
300 .unwrap();
301 let new_opened_logical_region_ids = r.new_opened_logical_region_ids().unwrap();
302 assert!(new_opened_logical_region_ids.is_empty());
303
304 metric_engine
306 .handle_request(
307 target_physical_region_id,
308 RegionRequest::Close(RegionCloseRequest::default()),
309 )
310 .await
311 .unwrap();
312 let physical_region_option = [(PHYSICAL_TABLE_METADATA_KEY.to_string(), String::new())]
313 .into_iter()
314 .collect();
315 metric_engine
316 .handle_request(
317 target_physical_region_id,
318 RegionRequest::Open(RegionOpenRequest {
319 engine: METRIC_ENGINE_NAME.to_string(),
320 table_dir: "/test_dir1".to_string(),
321 path_type: PathType::Bare,
322 options: physical_region_option,
323 skip_wal_replay: false,
324 checkpoint: None,
325 requirements: Default::default(),
326 }),
327 )
328 .await
329 .unwrap();
330 let logical_regions = env
331 .metadata_region()
332 .logical_regions(target_physical_region_id)
333 .await
334 .unwrap();
335 assert_eq!(logical_regions.len(), 2);
336 assert!(logical_regions.contains(&target_logical_region_id1));
337 assert!(logical_regions.contains(&target_logical_region_id2));
338 }
339
340 #[tokio::test]
341 async fn test_sync_region_from_region_with_no_files() {
342 common_telemetry::init_default_ut_logging();
343 let env = TestEnv::new().await;
344 let metric_engine = env.metric();
345 let source_physical_region_id = RegionId::new(1024, 0);
346 env.create_physical_region(source_physical_region_id, "/test_dir1", vec![])
347 .await;
348 let target_physical_region_id = RegionId::new(1024, 1);
349 env.create_physical_region(target_physical_region_id, "/test_dir1", vec![])
350 .await;
351 let r = metric_engine
352 .sync_region(
353 target_physical_region_id,
354 SyncRegionFromRequest::FromRegion {
355 source_region_id: source_physical_region_id,
356 parallelism: 1,
357 },
358 )
359 .await
360 .unwrap();
361 let new_opened_logical_region_ids = r.new_opened_logical_region_ids().unwrap();
362 assert!(new_opened_logical_region_ids.is_empty());
363 }
364
365 #[tokio::test]
366 async fn test_sync_region_from_region_source_not_exist() {
367 common_telemetry::init_default_ut_logging();
368 let env = TestEnv::new().await;
369 let metric_engine = env.metric();
370 let source_physical_region_id = RegionId::new(1024, 0);
371 let target_physical_region_id = RegionId::new(1024, 1);
372 env.create_physical_region(target_physical_region_id, "/test_dir1", vec![])
373 .await;
374 let err = metric_engine
375 .sync_region(
376 target_physical_region_id,
377 SyncRegionFromRequest::FromRegion {
378 source_region_id: source_physical_region_id,
379 parallelism: 1,
380 },
381 )
382 .await
383 .unwrap_err();
384 assert_eq!(err.status_code(), StatusCode::InvalidArguments);
385 }
386}