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