datanode/heartbeat/handler/
open_region.rs1use common_meta::instruction::{InstructionReply, OpenRegion, SimpleReply};
16use common_meta::wal_provider::prepare_wal_options;
17use common_telemetry::info;
18use store_api::path_utils::table_dir;
19use store_api::region_request::{PathType, RegionOpenRequest};
20use store_api::storage::RegionId;
21
22use crate::heartbeat::handler::{HandlerContext, InstructionHandler};
23
24pub struct OpenRegionsHandler {
25 pub open_region_parallelism: usize,
26}
27
28#[async_trait::async_trait]
29impl InstructionHandler for OpenRegionsHandler {
30 type Instruction = Vec<OpenRegion>;
31 async fn handle(
32 &self,
33 ctx: &HandlerContext,
34 open_regions: Self::Instruction,
35 ) -> Option<InstructionReply> {
36 let requests = open_regions
37 .into_iter()
38 .map(|open_region| {
39 let OpenRegion {
40 region_ident,
41 region_storage_path,
42 mut region_options,
43 region_wal_options,
44 skip_wal_replay,
45 reason,
46 requirements,
47 } = open_region;
48 let region_id = RegionId::new(region_ident.table_id, region_ident.region_number);
49 info!(
50 "Received open region instruction, region_id: {region_id}, reason: {reason:?}"
51 );
52 prepare_wal_options(&mut region_options, region_id, ®ion_wal_options);
53 let request = RegionOpenRequest {
54 engine: region_ident.engine,
55 table_dir: table_dir(®ion_storage_path, region_id.table_id()),
56 path_type: PathType::Bare,
57 options: region_options,
58 skip_wal_replay,
59 checkpoint: None,
60 requirements,
61 };
62 (region_id, request)
63 })
64 .collect::<Vec<_>>();
65
66 let result = ctx
67 .region_server
68 .handle_batch_open_requests(self.open_region_parallelism, requests, false)
69 .await;
70 let success = result.is_ok();
71 let error = result.as_ref().map_err(|e| format!("{e:?}")).err();
72
73 Some(InstructionReply::OpenRegions(SimpleReply {
74 result: success,
75 error,
76 }))
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use std::assert_matches;
83 use std::collections::HashMap;
84 use std::sync::Arc;
85
86 use common_meta::RegionIdent;
87 use common_meta::heartbeat::handler::{HandleControl, HeartbeatResponseHandler};
88 use common_meta::heartbeat::mailbox::MessageMeta;
89 use common_meta::instruction::{Instruction, OpenRegion};
90 use common_meta::kv_backend::memory::MemoryKvBackend;
91 use mito2::config::MitoConfig;
92 use mito2::engine::MITO_ENGINE_NAME;
93 use mito2::test_util::{CreateRequestBuilder, TestEnv};
94 use store_api::path_utils::table_dir;
95 use store_api::region_request::{RegionCloseRequest, RegionRequest, RegionRequirements};
96 use store_api::storage::RegionId;
97
98 use crate::heartbeat::handler::RegionHeartbeatResponseHandler;
99 use crate::heartbeat::handler::tests::HeartbeatResponseTestEnv;
100 use crate::tests::mock_region_server;
101
102 fn open_regions_instruction(
103 region_ids: impl IntoIterator<Item = RegionId>,
104 storage_path: &str,
105 ) -> Instruction {
106 let region_idents = region_ids
107 .into_iter()
108 .map(|region_id| {
109 OpenRegion::new(
110 RegionIdent {
111 datanode_id: 0,
112 table_id: region_id.table_id(),
113 region_number: region_id.region_number(),
114 engine: MITO_ENGINE_NAME.to_string(),
115 },
116 storage_path,
117 HashMap::new(),
118 HashMap::new(),
119 false,
120 None,
121 RegionRequirements::empty(),
122 )
123 })
124 .collect();
125
126 Instruction::OpenRegions(region_idents)
127 }
128
129 #[tokio::test]
130 async fn test_open_regions() {
131 common_telemetry::init_default_ut_logging();
132
133 let mut region_server = mock_region_server();
134 let kv_backend = Arc::new(MemoryKvBackend::new());
135 let heartbeat_handler =
136 RegionHeartbeatResponseHandler::new(region_server.clone(), kv_backend);
137 let mut engine_env = TestEnv::with_prefix("open-regions").await;
138 let engine = engine_env.create_engine(MitoConfig::default()).await;
139 region_server.register_engine(Arc::new(engine.clone()));
140 let region_id = RegionId::new(1024, 1);
141 let region_id1 = RegionId::new(1024, 2);
142 let storage_path = "test";
143 let builder = CreateRequestBuilder::new();
144 let mut create_req = builder.build();
145 create_req.table_dir = table_dir(storage_path, region_id.table_id());
146 region_server
147 .handle_request(region_id, RegionRequest::Create(create_req))
148 .await
149 .unwrap();
150 let mut create_req1 = builder.build();
151 create_req1.table_dir = table_dir(storage_path, region_id1.table_id());
152 region_server
153 .handle_request(region_id1, RegionRequest::Create(create_req1))
154 .await
155 .unwrap();
156 region_server
157 .handle_request(region_id, RegionRequest::Close(RegionCloseRequest {}))
158 .await
159 .unwrap();
160 region_server
161 .handle_request(region_id, RegionRequest::Close(RegionCloseRequest {}))
162 .await
163 .unwrap();
164
165 let meta = MessageMeta::new_test(1, "test", "dn-1", "me-0");
166 let instruction = open_regions_instruction([region_id, region_id1], storage_path);
167 let mut heartbeat_env = HeartbeatResponseTestEnv::new();
168 let mut ctx = heartbeat_env.create_handler_ctx((meta, Default::default(), instruction));
169 let control = heartbeat_handler.handle(&mut ctx).await.unwrap();
170 assert_matches!(control, HandleControl::Continue);
171 let (_, reply) = heartbeat_env.receiver.recv().await.unwrap();
172
173 let reply = reply.expect_open_regions_reply();
174 assert!(reply.result);
175 assert!(reply.error.is_none());
176
177 assert!(engine.is_region_exists(region_id));
178 assert!(engine.is_region_exists(region_id1));
179 }
180}