1use std::any::Any;
16use std::collections::HashMap;
17use std::sync::{Arc, RwLock};
18
19use api::region::RegionResponse;
20use async_trait::async_trait;
21use common_catalog::consts::FILE_ENGINE;
22use common_error::ext::BoxedError;
23use common_recordbatch::SendableRecordBatchStream;
24use common_telemetry::{error, info};
25use object_store::ObjectStore;
26use snafu::{OptionExt, ensure};
27use store_api::metadata::RegionMetadataRef;
28use store_api::region_engine::{
29 CopyRegionFromRequest, CopyRegionFromResponse, RegionEngine, RegionManifestInfo, RegionRole,
30 RegionScannerRef, RegionStatistic, RemapManifestsRequest, RemapManifestsResponse,
31 SetRegionRoleStateResponse, SetRegionRoleStateSuccess, SettableRegionRoleState,
32 SinglePartitionScanner, SyncManifestResponse,
33};
34use store_api::region_request::{
35 AffectedRows, RegionCloseRequest, RegionCreateRequest, RegionDropRequest, RegionOpenRequest,
36 RegionRequest,
37};
38use store_api::storage::{RegionId, ScanRequest, SequenceNumber};
39use tokio::sync::Mutex;
40
41use crate::config::EngineConfig;
42use crate::error::{
43 RegionNotFoundSnafu, Result as EngineResult, UnexpectedEngineSnafu, UnsupportedSnafu,
44};
45use crate::region::{FileRegion, FileRegionRef};
46
47pub struct FileRegionEngine {
48 inner: EngineInnerRef,
49}
50
51impl FileRegionEngine {
52 pub fn new(_config: EngineConfig, object_store: ObjectStore) -> Self {
53 Self {
54 inner: Arc::new(EngineInner::new(object_store)),
55 }
56 }
57
58 async fn handle_query(
59 &self,
60 region_id: RegionId,
61 request: ScanRequest,
62 ) -> Result<SendableRecordBatchStream, BoxedError> {
63 self.inner
64 .get_region(region_id)
65 .await
66 .context(RegionNotFoundSnafu { region_id })
67 .map_err(BoxedError::new)?
68 .query(request)
69 .map_err(BoxedError::new)
70 }
71}
72
73#[async_trait]
74impl RegionEngine for FileRegionEngine {
75 fn name(&self) -> &str {
76 FILE_ENGINE
77 }
78
79 async fn handle_request(
80 &self,
81 region_id: RegionId,
82 request: RegionRequest,
83 ) -> Result<RegionResponse, BoxedError> {
84 self.inner
85 .handle_request(region_id, request)
86 .await
87 .map_err(BoxedError::new)
88 }
89
90 async fn handle_query(
91 &self,
92 region_id: RegionId,
93 request: ScanRequest,
94 ) -> Result<RegionScannerRef, BoxedError> {
95 let stream = self.handle_query(region_id, request).await?;
96 let metadata = self.get_metadata(region_id).await?;
97 let scanner = Box::new(SinglePartitionScanner::new(stream, false, metadata));
99 Ok(scanner)
100 }
101
102 async fn get_metadata(&self, region_id: RegionId) -> Result<RegionMetadataRef, BoxedError> {
103 self.inner
104 .get_region(region_id)
105 .await
106 .map(|r| r.metadata())
107 .context(RegionNotFoundSnafu { region_id })
108 .map_err(BoxedError::new)
109 }
110
111 async fn stop(&self) -> Result<(), BoxedError> {
112 self.inner.stop().await.map_err(BoxedError::new)
113 }
114
115 fn region_statistic(&self, _: RegionId) -> Option<RegionStatistic> {
116 None
117 }
118
119 async fn get_committed_sequence(&self, _: RegionId) -> Result<SequenceNumber, BoxedError> {
120 Ok(Default::default())
121 }
122
123 fn set_region_role(&self, region_id: RegionId, role: RegionRole) -> Result<(), BoxedError> {
124 self.inner
125 .set_region_role(region_id, role)
126 .map_err(BoxedError::new)
127 }
128
129 async fn set_region_role_state_gracefully(
130 &self,
131 region_id: RegionId,
132 _region_role_state: SettableRegionRoleState,
133 ) -> Result<SetRegionRoleStateResponse, BoxedError> {
134 let exists = self.inner.get_region(region_id).await.is_some();
135
136 if exists {
137 Ok(SetRegionRoleStateResponse::success(
138 SetRegionRoleStateSuccess::file(),
139 ))
140 } else {
141 Ok(SetRegionRoleStateResponse::NotFound)
142 }
143 }
144
145 async fn sync_region(
146 &self,
147 _region_id: RegionId,
148 _manifest_info: RegionManifestInfo,
149 ) -> Result<SyncManifestResponse, BoxedError> {
150 Ok(SyncManifestResponse::NotSupported)
152 }
153
154 async fn remap_manifests(
155 &self,
156 _request: RemapManifestsRequest,
157 ) -> Result<RemapManifestsResponse, BoxedError> {
158 Err(BoxedError::new(
159 UnsupportedSnafu {
160 operation: "remap_manifests",
161 }
162 .build(),
163 ))
164 }
165
166 async fn copy_region_from(
167 &self,
168 _region_id: RegionId,
169 _request: CopyRegionFromRequest,
170 ) -> Result<CopyRegionFromResponse, BoxedError> {
171 Err(BoxedError::new(
172 UnsupportedSnafu {
173 operation: "copy_region_from",
174 }
175 .build(),
176 ))
177 }
178
179 fn role(&self, region_id: RegionId) -> Option<RegionRole> {
180 self.inner.state(region_id)
181 }
182
183 fn as_any(&self) -> &dyn Any {
184 self
185 }
186}
187
188struct EngineInner {
189 regions: RwLock<HashMap<RegionId, FileRegionRef>>,
193
194 region_mutex: Mutex<()>,
197
198 object_store: ObjectStore,
199}
200
201type EngineInnerRef = Arc<EngineInner>;
202
203impl EngineInner {
204 fn new(object_store: ObjectStore) -> Self {
205 Self {
206 regions: RwLock::new(HashMap::new()),
207 region_mutex: Mutex::new(()),
208 object_store,
209 }
210 }
211
212 async fn handle_request(
213 &self,
214 region_id: RegionId,
215 request: RegionRequest,
216 ) -> EngineResult<RegionResponse> {
217 let result = match request {
218 RegionRequest::Create(req) => self.handle_create(region_id, req).await,
219 RegionRequest::Drop(req) => self.handle_drop(region_id, req).await,
220 RegionRequest::Open(req) => self.handle_open(region_id, req).await,
221 RegionRequest::Close(req) => self.handle_close(region_id, req).await,
222 _ => UnsupportedSnafu {
223 operation: request.to_string(),
224 }
225 .fail(),
226 };
227 result.map(RegionResponse::new)
228 }
229
230 async fn stop(&self) -> EngineResult<()> {
231 let _lock = self.region_mutex.lock().await;
232 self.regions.write().unwrap().clear();
233 Ok(())
234 }
235
236 fn set_region_role(&self, _region_id: RegionId, _region_role: RegionRole) -> EngineResult<()> {
237 Ok(())
239 }
240
241 fn state(&self, region_id: RegionId) -> Option<RegionRole> {
242 if self.regions.read().unwrap().get(®ion_id).is_some() {
243 Some(RegionRole::Leader)
244 } else {
245 None
246 }
247 }
248}
249
250impl EngineInner {
251 async fn handle_create(
252 &self,
253 region_id: RegionId,
254 request: RegionCreateRequest,
255 ) -> EngineResult<AffectedRows> {
256 ensure!(
257 request.engine == FILE_ENGINE,
258 UnexpectedEngineSnafu {
259 engine: request.engine
260 }
261 );
262
263 if self.exists(region_id).await {
264 return Ok(0);
265 }
266
267 info!("Try to create region, region_id: {}", region_id);
268
269 let _lock = self.region_mutex.lock().await;
270 if self.exists(region_id).await {
272 return Ok(0);
273 }
274
275 let res = FileRegion::create(region_id, request, &self.object_store).await;
276 let region = res.inspect_err(|err| {
277 error!(
278 err;
279 "Failed to create region, region_id: {}",
280 region_id
281 );
282 })?;
283 self.regions.write().unwrap().insert(region_id, region);
284
285 info!("A new region is created, region_id: {}", region_id);
286 Ok(0)
287 }
288
289 async fn handle_open(
290 &self,
291 region_id: RegionId,
292 request: RegionOpenRequest,
293 ) -> EngineResult<AffectedRows> {
294 if self.exists(region_id).await {
295 return Ok(0);
296 }
297
298 info!("Try to open region, region_id: {}", region_id);
299
300 let _lock = self.region_mutex.lock().await;
301 if self.exists(region_id).await {
303 return Ok(0);
304 }
305
306 let res = FileRegion::open(region_id, request, &self.object_store).await;
307 let region = res.inspect_err(|err| {
308 error!(
309 err;
310 "Failed to open region, region_id: {}",
311 region_id
312 );
313 })?;
314 self.regions.write().unwrap().insert(region_id, region);
315
316 info!("Region opened, region_id: {}", region_id);
317 Ok(0)
318 }
319
320 async fn handle_close(
321 &self,
322 region_id: RegionId,
323 _request: RegionCloseRequest,
324 ) -> EngineResult<AffectedRows> {
325 let _lock = self.region_mutex.lock().await;
326
327 let mut regions = self.regions.write().unwrap();
328 if regions.remove(®ion_id).is_some() {
329 info!("Region closed, region_id: {}", region_id);
330 }
331
332 Ok(0)
333 }
334
335 async fn handle_drop(
336 &self,
337 region_id: RegionId,
338 _request: RegionDropRequest,
339 ) -> EngineResult<AffectedRows> {
340 if !self.exists(region_id).await {
341 return RegionNotFoundSnafu { region_id }.fail();
342 }
343
344 info!("Try to drop region, region_id: {}", region_id);
345
346 let _lock = self.region_mutex.lock().await;
347
348 let region = self.get_region(region_id).await;
349 if let Some(region) = region {
350 let res = FileRegion::drop(®ion, &self.object_store).await;
351 res.inspect_err(|err| {
352 error!(
353 err;
354 "Failed to drop region, region_id: {}",
355 region_id
356 );
357 })?;
358 }
359 let _ = self.regions.write().unwrap().remove(®ion_id);
360
361 info!("Region dropped, region_id: {}", region_id);
362 Ok(0)
363 }
364
365 async fn get_region(&self, region_id: RegionId) -> Option<FileRegionRef> {
366 self.regions.read().unwrap().get(®ion_id).cloned()
367 }
368
369 async fn exists(&self, region_id: RegionId) -> bool {
370 self.regions.read().unwrap().contains_key(®ion_id)
371 }
372}