file_engine/
engine.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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        // We don't support enabling append mode for file engine.
98        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        // File engine doesn't need to sync region manifest.
151        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    /// All regions opened by the engine.
190    ///
191    /// Writing to `regions` should also hold the `region_mutex`.
192    regions: RwLock<HashMap<RegionId, FileRegionRef>>,
193
194    /// Region mutex is used to protect the operations such as creating/opening/closing
195    /// a region, to avoid things like opening the same region simultaneously.
196    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        // TODO(zhongzc): Improve the semantics and implementation of this API.
238        Ok(())
239    }
240
241    fn state(&self, region_id: RegionId) -> Option<RegionRole> {
242        if self.regions.read().unwrap().get(&region_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        // Check again after acquiring the lock
271        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        // Check again after acquiring the lock
302        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(&region_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(&region, &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(&region_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(&region_id).cloned()
367    }
368
369    async fn exists(&self, region_id: RegionId) -> bool {
370        self.regions.read().unwrap().contains_key(&region_id)
371    }
372}