1mod alter;
16mod bulk_insert;
17mod catchup;
18mod close;
19mod create;
20mod drop;
21mod flush;
22mod open;
23mod options;
24mod put;
25mod read;
26mod region_metadata;
27mod staging;
28mod state;
29mod sync;
30
31use std::any::Any;
32use std::collections::HashMap;
33use std::sync::{Arc, RwLock};
34
35use api::region::RegionResponse;
36use async_trait::async_trait;
37use common_error::ext::{BoxedError, ErrorExt};
38use common_error::status_code::StatusCode;
39use common_runtime::RepeatedTask;
40use mito2::engine::MitoEngine;
41pub(crate) use options::IndexOptions;
42use snafu::{OptionExt, ResultExt};
43pub(crate) use state::MetricEngineState;
44use store_api::metadata::RegionMetadataRef;
45use store_api::metric_engine_consts::METRIC_ENGINE_NAME;
46use store_api::region_engine::{
47 BatchResponses, RegionEngine, RegionRole, RegionScannerRef, RegionStatistic,
48 RemapManifestsRequest, RemapManifestsResponse, SetRegionRoleStateResponse,
49 SetRegionRoleStateSuccess, SettableRegionRoleState, SyncRegionFromRequest,
50 SyncRegionFromResponse,
51};
52use store_api::region_request::{
53 AffectedRows, BatchRegionDdlRequest, RegionCatchupRequest, RegionOpenRequest, RegionPutRequest,
54 RegionRequest, RegionTruncateRequest,
55};
56use store_api::storage::{RegionId, ScanRequest, SequenceNumber};
57
58use crate::config::EngineConfig;
59use crate::data_region::DataRegion;
60use crate::error::{
61 self, Error, Result, StartRepeatedTaskSnafu, UnsupportedRegionRequestSnafu,
62 UnsupportedRemapManifestsRequestSnafu,
63};
64use crate::metadata_region::MetadataRegion;
65use crate::repeated_task::FlushMetadataRegionTask;
66use crate::row_modifier::RowModifier;
67use crate::utils::{self, get_region_statistic};
68
69#[cfg_attr(doc, aquamarine::aquamarine)]
70#[derive(Clone)]
132pub struct MetricEngine {
133 inner: Arc<MetricEngineInner>,
134}
135
136#[async_trait]
137impl RegionEngine for MetricEngine {
138 fn name(&self) -> &str {
140 METRIC_ENGINE_NAME
141 }
142
143 async fn handle_batch_open_requests(
144 &self,
145 parallelism: usize,
146 requests: Vec<(RegionId, RegionOpenRequest)>,
147 ) -> Result<BatchResponses, BoxedError> {
148 self.inner
149 .handle_batch_open_requests(parallelism, requests)
150 .await
151 .map_err(BoxedError::new)
152 }
153
154 async fn handle_batch_catchup_requests(
155 &self,
156 parallelism: usize,
157 requests: Vec<(RegionId, RegionCatchupRequest)>,
158 ) -> Result<BatchResponses, BoxedError> {
159 self.inner
160 .handle_batch_catchup_requests(parallelism, requests)
161 .await
162 .map_err(BoxedError::new)
163 }
164
165 async fn handle_batch_ddl_requests(
166 &self,
167 batch_request: BatchRegionDdlRequest,
168 ) -> Result<RegionResponse, BoxedError> {
169 match batch_request {
170 BatchRegionDdlRequest::Create(requests) => {
171 let mut extension_return_value = HashMap::new();
172 let rows = self
173 .inner
174 .create_regions(requests, &mut extension_return_value)
175 .await
176 .map_err(BoxedError::new)?;
177
178 Ok(RegionResponse {
179 affected_rows: rows,
180 extensions: extension_return_value,
181 metadata: Vec::new(),
182 })
183 }
184 BatchRegionDdlRequest::Alter(requests) => {
185 let mut extension_return_value = HashMap::new();
186 let rows = self
187 .inner
188 .alter_regions(requests, &mut extension_return_value)
189 .await
190 .map_err(BoxedError::new)?;
191
192 Ok(RegionResponse {
193 affected_rows: rows,
194 extensions: extension_return_value,
195 metadata: Vec::new(),
196 })
197 }
198 BatchRegionDdlRequest::Drop(requests) => {
199 self.handle_requests(
200 requests
201 .into_iter()
202 .map(|(region_id, req)| (region_id, RegionRequest::Drop(req))),
203 )
204 .await
205 }
206 }
207 }
208
209 async fn handle_request(
211 &self,
212 region_id: RegionId,
213 request: RegionRequest,
214 ) -> Result<RegionResponse, BoxedError> {
215 let mut extension_return_value = HashMap::new();
216
217 let result = match request {
218 RegionRequest::EnterStaging(_) => {
219 if self.inner.is_physical_region(region_id) {
220 self.handle_enter_staging_request(region_id, request).await
221 } else {
222 UnsupportedRegionRequestSnafu { request }.fail()
223 }
224 }
225 RegionRequest::ApplyStagingManifest(_) => {
226 if self.inner.is_physical_region(region_id) {
227 return self.inner.mito.handle_request(region_id, request).await;
228 } else {
229 UnsupportedRegionRequestSnafu { request }.fail()
230 }
231 }
232 RegionRequest::Put(put) => self.inner.put_region(region_id, put).await,
233 RegionRequest::Create(create) => {
234 self.inner
235 .create_regions(vec![(region_id, create)], &mut extension_return_value)
236 .await
237 }
238 RegionRequest::Drop(drop) => self.inner.drop_region(region_id, drop).await,
239 RegionRequest::Open(open) => self.inner.open_region(region_id, open).await,
240 RegionRequest::CleanUp(clean_up) => {
241 self.inner.clean_up_region(region_id, clean_up).await
242 }
243 RegionRequest::Close(close) => self.inner.close_region(region_id, close).await,
244 RegionRequest::Alter(alter) => {
245 self.inner
246 .alter_regions(vec![(region_id, alter)], &mut extension_return_value)
247 .await
248 }
249 RegionRequest::Compact(_) => {
250 if self.inner.is_physical_region(region_id) {
251 self.inner
252 .mito
253 .handle_request(region_id, request)
254 .await
255 .context(error::MitoFlushOperationSnafu)
256 .map(|response| response.affected_rows)
257 } else {
258 UnsupportedRegionRequestSnafu { request }.fail()
259 }
260 }
261 RegionRequest::Flush(req) => self.inner.flush_region(region_id, req).await,
262 RegionRequest::BuildIndex(_) => {
263 if self.inner.is_physical_region(region_id) {
264 self.inner
265 .mito
266 .handle_request(region_id, request)
267 .await
268 .context(error::MitoFlushOperationSnafu)
269 .map(|response| response.affected_rows)
270 } else {
271 UnsupportedRegionRequestSnafu { request }.fail()
272 }
273 }
274 RegionRequest::Truncate(RegionTruncateRequest::Unflushed) => {
275 if self.inner.is_physical_region(region_id) {
276 self.inner
277 .mito
278 .handle_request(
279 utils::to_data_region_id(region_id),
280 RegionRequest::Truncate(RegionTruncateRequest::Unflushed),
281 )
282 .await
283 .context(error::MitoTruncateOperationSnafu)
284 .map(|response| response.affected_rows)
285 } else {
286 UnsupportedRegionRequestSnafu {
287 request: RegionRequest::Truncate(RegionTruncateRequest::Unflushed),
288 }
289 .fail()
290 }
291 }
292 RegionRequest::Truncate(request) => UnsupportedRegionRequestSnafu {
293 request: RegionRequest::Truncate(request),
294 }
295 .fail(),
296 RegionRequest::Delete(delete) => self.inner.delete_region(region_id, delete).await,
297 RegionRequest::Catchup(_) => {
298 let mut response = self
299 .inner
300 .handle_batch_catchup_requests(
301 1,
302 vec![(region_id, RegionCatchupRequest::default())],
303 )
304 .await
305 .map_err(BoxedError::new)?;
306 debug_assert_eq!(response.len(), 1);
307 let (resp_region_id, response) = response
308 .pop()
309 .context(error::UnexpectedRequestSnafu {
310 reason: "expected 1 response, but got zero responses",
311 })
312 .map_err(BoxedError::new)?;
313 debug_assert_eq!(region_id, resp_region_id);
314 return response;
315 }
316 RegionRequest::BulkInserts(bulk) => {
317 self.inner.bulk_insert_region(region_id, bulk).await
318 }
319 };
320
321 result.map_err(BoxedError::new).map(|rows| RegionResponse {
322 affected_rows: rows,
323 extensions: extension_return_value,
324 metadata: Vec::new(),
325 })
326 }
327
328 async fn handle_query(
329 &self,
330 region_id: RegionId,
331 request: ScanRequest,
332 ) -> Result<RegionScannerRef, BoxedError> {
333 self.handle_query(region_id, request).await
334 }
335
336 async fn get_committed_sequence(
337 &self,
338 region_id: RegionId,
339 ) -> Result<SequenceNumber, BoxedError> {
340 self.inner
341 .get_last_seq_num(region_id)
342 .await
343 .map_err(BoxedError::new)
344 }
345
346 async fn get_metadata(&self, region_id: RegionId) -> Result<RegionMetadataRef, BoxedError> {
348 self.inner
349 .load_region_metadata(region_id)
350 .await
351 .map_err(BoxedError::new)
352 }
353
354 fn region_statistic(&self, region_id: RegionId) -> Option<RegionStatistic> {
358 if self.inner.is_physical_region(region_id) {
359 get_region_statistic(&self.inner.mito, region_id)
360 } else {
361 None
362 }
363 }
364
365 async fn stop(&self) -> Result<(), BoxedError> {
367 Ok(())
369 }
370
371 fn set_region_role(&self, region_id: RegionId, role: RegionRole) -> Result<(), BoxedError> {
372 for x in [
374 utils::to_metadata_region_id(region_id),
375 utils::to_data_region_id(region_id),
376 ] {
377 if let Err(e) = self.inner.mito.set_region_role(x, role)
378 && e.status_code() != StatusCode::RegionNotFound
379 {
380 return Err(e);
381 }
382 }
383 Ok(())
384 }
385
386 async fn sync_region(
387 &self,
388 region_id: RegionId,
389 request: SyncRegionFromRequest,
390 ) -> Result<SyncRegionFromResponse, BoxedError> {
391 match request {
392 SyncRegionFromRequest::FromManifest(manifest_info) => self
393 .inner
394 .sync_region_from_manifest(region_id, manifest_info)
395 .await
396 .map_err(BoxedError::new),
397 SyncRegionFromRequest::FromRegion {
398 source_region_id,
399 parallelism,
400 } => {
401 if self.inner.is_physical_region(region_id) {
402 self.inner
403 .sync_region_from_region(region_id, source_region_id, parallelism)
404 .await
405 .map_err(BoxedError::new)
406 } else {
407 Err(BoxedError::new(
408 error::UnsupportedSyncRegionFromRequestSnafu { region_id }.build(),
409 ))
410 }
411 }
412 }
413 }
414
415 async fn remap_manifests(
416 &self,
417 request: RemapManifestsRequest,
418 ) -> Result<RemapManifestsResponse, BoxedError> {
419 let region_id = request.region_id;
420 if self.inner.is_physical_region(region_id) {
421 self.inner.mito.remap_manifests(request).await
422 } else {
423 Err(BoxedError::new(
424 UnsupportedRemapManifestsRequestSnafu { region_id }.build(),
425 ))
426 }
427 }
428
429 async fn set_region_role_state_gracefully(
430 &self,
431 region_id: RegionId,
432 region_role_state: SettableRegionRoleState,
433 ) -> std::result::Result<SetRegionRoleStateResponse, BoxedError> {
434 let metadata_result = match self
435 .inner
436 .mito
437 .set_region_role_state_gracefully(
438 utils::to_metadata_region_id(region_id),
439 region_role_state,
440 )
441 .await?
442 {
443 SetRegionRoleStateResponse::Success(success) => success,
444 SetRegionRoleStateResponse::NotFound => {
445 return Ok(SetRegionRoleStateResponse::NotFound);
446 }
447 SetRegionRoleStateResponse::InvalidTransition(error) => {
448 return Ok(SetRegionRoleStateResponse::InvalidTransition(error));
449 }
450 };
451
452 let data_result = match self
453 .inner
454 .mito
455 .set_region_role_state_gracefully(region_id, region_role_state)
456 .await?
457 {
458 SetRegionRoleStateResponse::Success(success) => success,
459 SetRegionRoleStateResponse::NotFound => {
460 return Ok(SetRegionRoleStateResponse::NotFound);
461 }
462 SetRegionRoleStateResponse::InvalidTransition(error) => {
463 return Ok(SetRegionRoleStateResponse::InvalidTransition(error));
464 }
465 };
466
467 Ok(SetRegionRoleStateResponse::success(
468 SetRegionRoleStateSuccess::metric(
469 data_result.last_entry_id().unwrap_or_default(),
470 metadata_result.last_entry_id().unwrap_or_default(),
471 ),
472 ))
473 }
474
475 fn role(&self, region_id: RegionId) -> Option<RegionRole> {
479 if self.inner.is_physical_region(region_id) {
480 self.inner.mito.role(region_id)
481 } else {
482 None
483 }
484 }
485
486 fn as_any(&self) -> &dyn Any {
487 self
488 }
489}
490
491#[derive(Debug)]
495pub struct PutGroupResult {
496 pub request_indices: Vec<usize>,
498 pub result: Result<AffectedRows>,
500}
501
502impl MetricEngine {
503 pub fn try_new(mito: MitoEngine, mut config: EngineConfig) -> Result<Self> {
504 let metadata_region = MetadataRegion::new(mito.clone());
505 let data_region = DataRegion::new(mito.clone());
506 let state = Arc::new(RwLock::default());
507 config.sanitize();
508 let flush_interval = config.flush_metadata_region_interval;
509 let inner = Arc::new(MetricEngineInner {
510 mito: mito.clone(),
511 metadata_region,
512 data_region,
513 state: state.clone(),
514 row_modifier: RowModifier::default(),
515 flush_task: RepeatedTask::new(
516 flush_interval,
517 Box::new(FlushMetadataRegionTask {
518 state: state.clone(),
519 mito: mito.clone(),
520 }),
521 ),
522 });
523 inner
524 .flush_task
525 .start(common_runtime::global_runtime())
526 .context(StartRepeatedTaskSnafu { name: "flush_task" })?;
527 Ok(Self { inner })
528 }
529
530 pub fn mito(&self) -> MitoEngine {
531 self.inner.mito.clone()
532 }
533
534 pub async fn put_regions_batch(
536 &self,
537 requests: impl ExactSizeIterator<Item = (RegionId, RegionPutRequest)>,
538 ) -> Vec<PutGroupResult> {
539 self.inner.put_regions_batch(requests).await
540 }
541
542 pub async fn logical_regions(&self, physical_region_id: RegionId) -> Result<Vec<RegionId>> {
544 self.inner
545 .metadata_region
546 .logical_regions(physical_region_id)
547 .await
548 }
549
550 async fn handle_query(
552 &self,
553 region_id: RegionId,
554 request: ScanRequest,
555 ) -> Result<RegionScannerRef, BoxedError> {
556 self.inner
557 .read_region(region_id, request)
558 .await
559 .map_err(BoxedError::new)
560 }
561
562 async fn handle_requests(
563 &self,
564 requests: impl IntoIterator<Item = (RegionId, RegionRequest)>,
565 ) -> Result<RegionResponse, BoxedError> {
566 let mut affected_rows = 0;
567 let mut extensions = HashMap::new();
568 for (region_id, request) in requests {
569 let response = self.handle_request(region_id, request).await?;
570 affected_rows += response.affected_rows;
571 extensions.extend(response.extensions);
572 }
573
574 Ok(RegionResponse {
575 affected_rows,
576 extensions,
577 metadata: Vec::new(),
578 })
579 }
580}
581
582#[cfg(test)]
583impl MetricEngine {
584 pub async fn scan_to_stream(
585 &self,
586 region_id: RegionId,
587 request: ScanRequest,
588 ) -> Result<common_recordbatch::SendableRecordBatchStream, BoxedError> {
589 self.inner.scan_to_stream(region_id, request).await
590 }
591}
592
593struct MetricEngineInner {
594 mito: MitoEngine,
595 metadata_region: MetadataRegion,
596 data_region: DataRegion,
597 state: Arc<RwLock<MetricEngineState>>,
598 row_modifier: RowModifier,
599 flush_task: RepeatedTask<Error>,
600}
601
602#[cfg(test)]
603mod test {
604 use std::assert_matches;
605 use std::collections::HashMap;
606
607 use api::v1::Rows;
608 use common_recordbatch::RecordBatches;
609 use common_telemetry::info;
610 use common_wal::options::{KafkaWalOptions, WalOptions};
611 use mito2::sst::location::region_dir_from_table_dir;
612 use mito2::test_util::{kafka_log_store_factory, prepare_test_for_kafka_log_store};
613 use store_api::metric_engine_consts::PHYSICAL_TABLE_METADATA_KEY;
614 use store_api::mito_engine_options::WAL_OPTIONS_KEY;
615 use store_api::region_request::{
616 PathType, RegionCleanUpRequest, RegionCloseRequest, RegionDropRequest, RegionFlushRequest,
617 RegionOpenRequest, RegionPutRequest, RegionRequest, RegionTruncateRequest,
618 };
619
620 use super::*;
621 use crate::maybe_skip_kafka_log_store_integration_test;
622 use crate::test_util::{
623 TestEnv, build_rows, create_logical_region_request, row_schema_with_tags,
624 };
625
626 #[tokio::test]
627 async fn test_build_series_index_forwarding() {
628 use api::v1::region::build_index_request;
629 use store_api::region_request::RegionBuildIndexRequest;
630
631 let env = TestEnv::new().await;
632 env.init_metric_region().await;
633 let request = || {
634 RegionRequest::BuildIndex(RegionBuildIndexRequest {
635 options: Some(build_index_request::Options::SeriesIndex(Default::default())),
636 })
637 };
638 let error = env
640 .metric()
641 .handle_request(env.default_physical_region_id(), request())
642 .await
643 .unwrap_err();
644 let error = format!("{error:?}");
645 assert!(error.contains("series index is disabled"), "{error}");
646 let error = env
647 .metric()
648 .handle_request(env.default_logical_region_id(), request())
649 .await
650 .unwrap_err();
651 assert_eq!(
652 common_error::status_code::StatusCode::Unsupported,
653 error.status_code()
654 );
655 }
656
657 #[tokio::test]
658 async fn test_discard_unflushed_data_only() {
659 let env = TestEnv::new().await;
660 env.init_metric_region().await;
661
662 let engine = env.metric();
663 let mito = env.mito();
664 let physical_region_id = env.default_physical_region_id();
665 let logical_region_id = env.default_logical_region_id();
666 let data_region_id = utils::to_data_region_id(physical_region_id);
667 let metadata_region_id = utils::to_metadata_region_id(physical_region_id);
668
669 let metadata_memtable_size = mito
670 .region_statistic(metadata_region_id)
671 .unwrap()
672 .memtable_size;
673 assert!(metadata_memtable_size > 0);
674
675 engine
676 .handle_request(
677 logical_region_id,
678 RegionRequest::Put(RegionPutRequest {
679 skip_wal: false,
680 rows: Rows {
681 schema: row_schema_with_tags(&["job"]),
682 rows: build_rows(1, 5),
683 },
684 hint: None,
685 partition_expr_version: None,
686 }),
687 )
688 .await
689 .unwrap();
690 assert!(mito.region_statistic(data_region_id).unwrap().memtable_size > 0);
691
692 engine
693 .handle_request(
694 physical_region_id,
695 RegionRequest::Truncate(RegionTruncateRequest::Unflushed),
696 )
697 .await
698 .unwrap();
699
700 assert_eq!(
701 0,
702 mito.region_statistic(data_region_id).unwrap().memtable_size
703 );
704 assert_eq!(
705 metadata_memtable_size,
706 mito.region_statistic(metadata_region_id)
707 .unwrap()
708 .memtable_size
709 );
710
711 let stream = engine
712 .scan_to_stream(logical_region_id, ScanRequest::default())
713 .await
714 .unwrap();
715 let batches = RecordBatches::try_collect(stream).await.unwrap();
716 assert_eq!(
717 0,
718 batches.iter().map(|batch| batch.num_rows()).sum::<usize>()
719 );
720
721 engine
723 .handle_request(
724 logical_region_id,
725 RegionRequest::Truncate(RegionTruncateRequest::Unflushed),
726 )
727 .await
728 .unwrap_err();
729 }
730
731 #[tokio::test]
732 async fn close_open_regions() {
733 let env = TestEnv::new().await;
734 env.init_metric_region().await;
735 let engine = env.metric();
736
737 let physical_region_id = env.default_physical_region_id();
739 engine
740 .handle_request(
741 physical_region_id,
742 RegionRequest::Close(RegionCloseRequest::default()),
743 )
744 .await
745 .unwrap();
746
747 let physical_region_option = [(PHYSICAL_TABLE_METADATA_KEY.to_string(), String::new())]
749 .into_iter()
750 .collect();
751 let open_request = RegionOpenRequest {
752 engine: METRIC_ENGINE_NAME.to_string(),
753 table_dir: TestEnv::default_table_dir(),
754 path_type: PathType::Bare, options: physical_region_option,
756 skip_wal_replay: false,
757 checkpoint: None,
758 requirements: Default::default(),
759 };
760 engine
761 .handle_request(physical_region_id, RegionRequest::Open(open_request))
762 .await
763 .unwrap();
764
765 let nonexistent_region_id = RegionId::new(12313, 12);
767 engine
768 .handle_request(
769 nonexistent_region_id,
770 RegionRequest::Close(RegionCloseRequest::default()),
771 )
772 .await
773 .unwrap();
774
775 let invalid_open_request = RegionOpenRequest {
777 engine: METRIC_ENGINE_NAME.to_string(),
778 table_dir: TestEnv::default_table_dir(),
779 path_type: PathType::Bare, options: HashMap::new(),
781 skip_wal_replay: false,
782 checkpoint: None,
783 requirements: Default::default(),
784 };
785 engine
786 .handle_request(
787 nonexistent_region_id,
788 RegionRequest::Open(invalid_open_request),
789 )
790 .await
791 .unwrap();
792 }
793
794 #[tokio::test]
795 async fn test_offline_cleanup_physical_region() {
796 let env = TestEnv::new().await;
797 env.init_metric_region().await;
798 let engine = env.metric();
799 let mito = env.mito();
800 let physical_region_id = env.default_physical_region_id();
801 let metadata_region_id = crate::utils::to_metadata_region_id(physical_region_id);
802 let data_region_id = crate::utils::to_data_region_id(physical_region_id);
803
804 engine
805 .handle_request(
806 physical_region_id,
807 RegionRequest::Close(RegionCloseRequest::default()),
808 )
809 .await
810 .unwrap();
811
812 let object_store = env.get_object_store().unwrap();
813 let metadata_region_dir = region_dir_from_table_dir(
814 &TestEnv::default_table_dir(),
815 metadata_region_id,
816 PathType::Metadata,
817 );
818 let data_region_dir = region_dir_from_table_dir(
819 &TestEnv::default_table_dir(),
820 data_region_id,
821 PathType::Data,
822 );
823 assert!(object_store.exists(&metadata_region_dir).await.unwrap());
824 assert!(object_store.exists(&data_region_dir).await.unwrap());
825
826 let physical_region_option = [(PHYSICAL_TABLE_METADATA_KEY.to_string(), String::new())]
827 .into_iter()
828 .collect();
829 let clean_up_request = RegionCleanUpRequest {
830 engine: METRIC_ENGINE_NAME.to_string(),
831 table_dir: TestEnv::default_table_dir(),
832 path_type: PathType::Bare,
833 options: physical_region_option,
834 };
835 engine
836 .handle_request(physical_region_id, RegionRequest::CleanUp(clean_up_request))
837 .await
838 .unwrap();
839
840 assert!(!mito.is_region_exists(metadata_region_id));
841 assert!(!mito.is_region_exists(data_region_id));
842 assert!(!object_store.exists(&metadata_region_dir).await.unwrap());
843 assert!(!object_store.exists(&data_region_dir).await.unwrap());
844 }
845
846 #[tokio::test]
847 async fn test_role() {
848 let env = TestEnv::new().await;
849 env.init_metric_region().await;
850
851 let logical_region_id = env.default_logical_region_id();
852 let physical_region_id = env.default_physical_region_id();
853
854 assert!(env.metric().role(logical_region_id).is_none());
855 assert!(env.metric().role(physical_region_id).is_some());
856 }
857
858 #[tokio::test]
859 async fn test_region_disk_usage() {
860 let env = TestEnv::new().await;
861 env.init_metric_region().await;
862
863 let logical_region_id = env.default_logical_region_id();
864 let physical_region_id = env.default_physical_region_id();
865
866 assert!(env.metric().region_statistic(logical_region_id).is_none());
867 assert!(env.metric().region_statistic(physical_region_id).is_some());
868 }
869
870 #[tokio::test]
871 async fn test_open_region_failure() {
872 let env = TestEnv::new().await;
873 env.init_metric_region().await;
874 let physical_region_id = env.default_physical_region_id();
875
876 let metric_engine = env.metric();
877 metric_engine
878 .handle_request(
879 physical_region_id,
880 RegionRequest::Flush(RegionFlushRequest::default()),
881 )
882 .await
883 .unwrap();
884
885 let path = region_dir_from_table_dir(
886 &TestEnv::default_table_dir(),
887 physical_region_id,
888 PathType::Metadata,
889 );
890 let object_store = env.get_object_store().unwrap();
891 let list = object_store.list(&path).await.unwrap();
892 for entry in list {
894 if entry.metadata().is_dir() {
895 continue;
896 }
897 if entry.name().ends_with("parquet") {
898 info!("deleting {}", entry.path());
899 object_store.delete(entry.path()).await.unwrap();
900 }
901 }
902
903 let physical_region_option = [(PHYSICAL_TABLE_METADATA_KEY.to_string(), String::new())]
904 .into_iter()
905 .collect();
906 let open_request = RegionOpenRequest {
907 engine: METRIC_ENGINE_NAME.to_string(),
908 table_dir: TestEnv::default_table_dir(),
909 path_type: PathType::Bare,
910 options: physical_region_option,
911 skip_wal_replay: false,
912 checkpoint: None,
913 requirements: Default::default(),
914 };
915 metric_engine
918 .handle_request(physical_region_id, RegionRequest::Open(open_request))
919 .await
920 .unwrap();
921
922 metric_engine
924 .handle_request(
925 physical_region_id,
926 RegionRequest::Close(RegionCloseRequest::default()),
927 )
928 .await
929 .unwrap();
930
931 let physical_region_option = [(PHYSICAL_TABLE_METADATA_KEY.to_string(), String::new())]
933 .into_iter()
934 .collect();
935 let open_request = RegionOpenRequest {
936 engine: METRIC_ENGINE_NAME.to_string(),
937 table_dir: TestEnv::default_table_dir(),
938 path_type: PathType::Bare,
939 options: physical_region_option,
940 skip_wal_replay: false,
941 checkpoint: None,
942 requirements: Default::default(),
943 };
944 let err = metric_engine
945 .handle_request(physical_region_id, RegionRequest::Open(open_request))
946 .await
947 .unwrap_err();
948 assert_eq!(err.status_code(), StatusCode::StorageUnavailable);
950
951 let mito_engine = metric_engine.mito();
952 let data_region_id = utils::to_data_region_id(physical_region_id);
953 let metadata_region_id = utils::to_metadata_region_id(physical_region_id);
954 let err = mito_engine.get_metadata(data_region_id).await.unwrap_err();
956 assert_eq!(err.status_code(), StatusCode::RegionNotFound);
957 let err = mito_engine
958 .get_metadata(metadata_region_id)
959 .await
960 .unwrap_err();
961 assert_eq!(err.status_code(), StatusCode::RegionNotFound);
962 }
963
964 #[tokio::test]
965 async fn test_catchup_regions() {
966 common_telemetry::init_default_ut_logging();
967 maybe_skip_kafka_log_store_integration_test!();
968 let kafka_log_store_factory = kafka_log_store_factory().unwrap();
969 let mito_env = mito2::test_util::TestEnv::new()
970 .await
971 .with_log_store_factory(kafka_log_store_factory.clone());
972 let env = TestEnv::with_mito_env(mito_env).await;
973 let table_dir = |region_id| format!("table/{region_id}");
974 let mut physical_region_ids = vec![];
975 let mut logical_region_ids = vec![];
976
977 let num_topics = 3;
978 let num_physical_regions = 8;
979 let num_logical_regions = 16;
980 let parallelism = 2;
981 let mut topics = Vec::with_capacity(num_topics);
982 for _ in 0..num_topics {
983 let topic = prepare_test_for_kafka_log_store(&kafka_log_store_factory)
984 .await
985 .unwrap();
986 topics.push(topic);
987 }
988
989 let topic_idx = |id| (id as usize) % num_topics;
990 for i in 0..num_physical_regions {
992 let physical_region_id = RegionId::new(1, i);
993 physical_region_ids.push(physical_region_id);
994
995 let wal_options = WalOptions::Kafka(KafkaWalOptions::new(topics[topic_idx(i)].clone()));
996 env.create_physical_region(
997 physical_region_id,
998 &table_dir(physical_region_id),
999 vec![(
1000 WAL_OPTIONS_KEY.to_string(),
1001 serde_json::to_string(&wal_options).unwrap(),
1002 )],
1003 )
1004 .await;
1005 for j in 0..num_logical_regions {
1007 let logical_region_id = RegionId::new(1024 + i, j);
1008 logical_region_ids.push(logical_region_id);
1009 env.create_logical_region(physical_region_id, logical_region_id)
1010 .await;
1011 }
1012 }
1013
1014 let metric_engine = env.metric();
1015 for region_id in logical_region_ids.iter().chain(physical_region_ids.iter()) {
1017 metric_engine
1018 .handle_request(
1019 *region_id,
1020 RegionRequest::Close(RegionCloseRequest::default()),
1021 )
1022 .await
1023 .unwrap();
1024 }
1025
1026 let requests = physical_region_ids
1028 .iter()
1029 .enumerate()
1030 .map(|(idx, region_id)| {
1031 let mut options = HashMap::new();
1032 let wal_options =
1033 WalOptions::Kafka(KafkaWalOptions::new(topics[topic_idx(idx as u32)].clone()));
1034 options.insert(PHYSICAL_TABLE_METADATA_KEY.to_string(), String::new());
1035 options.insert(
1036 WAL_OPTIONS_KEY.to_string(),
1037 serde_json::to_string(&wal_options).unwrap(),
1038 );
1039 (
1040 *region_id,
1041 RegionOpenRequest {
1042 engine: METRIC_ENGINE_NAME.to_string(),
1043 table_dir: table_dir(*region_id),
1044 path_type: PathType::Bare,
1045 options: options.clone(),
1046 skip_wal_replay: true,
1047 checkpoint: None,
1048 requirements: Default::default(),
1049 },
1050 )
1051 })
1052 .collect::<Vec<_>>();
1053 info!("Open batch regions with parallelism: {parallelism}");
1054 metric_engine
1055 .handle_batch_open_requests(parallelism, requests)
1056 .await
1057 .unwrap();
1058 {
1059 let state = metric_engine.inner.state.read().unwrap();
1060 for logical_region in &logical_region_ids {
1061 assert!(!state.logical_regions().contains_key(logical_region));
1062 }
1063 }
1064
1065 let catch_requests = physical_region_ids
1066 .iter()
1067 .map(|region_id| {
1068 (
1069 *region_id,
1070 RegionCatchupRequest {
1071 set_writable: true,
1072 ..Default::default()
1073 },
1074 )
1075 })
1076 .collect::<Vec<_>>();
1077 metric_engine
1078 .handle_batch_catchup_requests(parallelism, catch_requests)
1079 .await
1080 .unwrap();
1081 {
1082 let state = metric_engine.inner.state.read().unwrap();
1083 for logical_region in &logical_region_ids {
1084 assert!(state.logical_regions().contains_key(logical_region));
1085 }
1086 }
1087 }
1088
1089 #[tokio::test]
1090 async fn test_drop_region() {
1091 let env = TestEnv::new().await;
1092 let engine = env.metric();
1093 let physical_region_id1 = RegionId::new(1024, 0);
1094 let logical_region_id1 = RegionId::new(1025, 0);
1095 env.create_physical_region(physical_region_id1, "/test_dir1", vec![])
1096 .await;
1097 let region_create_request1 =
1098 create_logical_region_request(&["job"], physical_region_id1, "logical1");
1099 engine
1100 .handle_batch_ddl_requests(BatchRegionDdlRequest::Create(vec![(
1101 logical_region_id1,
1102 region_create_request1,
1103 )]))
1104 .await
1105 .unwrap();
1106 let err = engine
1107 .handle_request(
1108 physical_region_id1,
1109 RegionRequest::Drop(RegionDropRequest {
1110 fast_path: false,
1111 force: false,
1112 partial_drop: false,
1113 }),
1114 )
1115 .await
1116 .unwrap_err();
1117 assert_matches!(
1118 err.as_any().downcast_ref::<Error>().unwrap(),
1119 &Error::PhysicalRegionBusy { .. }
1120 );
1121
1122 engine
1123 .handle_request(
1124 physical_region_id1,
1125 RegionRequest::Drop(RegionDropRequest {
1126 fast_path: false,
1127 force: true,
1128 partial_drop: false,
1129 }),
1130 )
1131 .await
1132 .unwrap();
1133 assert!(
1134 engine
1135 .inner
1136 .state
1137 .read()
1138 .unwrap()
1139 .physical_region_states()
1140 .get(&physical_region_id1)
1141 .is_none()
1142 );
1143 assert!(
1144 engine
1145 .inner
1146 .state
1147 .read()
1148 .unwrap()
1149 .logical_regions()
1150 .get(&logical_region_id1)
1151 .is_none()
1152 );
1153 }
1154}