Skip to main content

mito2/worker/
handle_close.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
15//! Handling close request.
16
17use common_telemetry::info;
18use store_api::logstore::LogStore;
19use store_api::region_request::{RegionCloseRequest, RegionFlushReason, RegionFlushRequest};
20use store_api::storage::RegionId;
21
22use crate::request::{DdlRequest, OptionOutputTx, SenderDdlRequest};
23use crate::worker::RegionWorkerLoop;
24
25impl<S: LogStore> RegionWorkerLoop<S> {
26    pub(crate) async fn handle_close_request(
27        &mut self,
28        region_id: RegionId,
29        request: RegionCloseRequest,
30        sender: OptionOutputTx,
31    ) {
32        let Some(region) = self.regions.get_region(region_id) else {
33            sender.send(Ok(0));
34            return;
35        };
36
37        info!("Try to close region {}, worker: {}", region_id, self.id);
38
39        // Memtables containing skip-WAL writes must be flushed before closing.
40        // Followers cannot flush; explicit flush-on-close and region WAL options
41        // retain their existing behavior.
42        let version = region.version();
43        if (request.flush_on_close || region.skip_wal() || version.memtables.has_skip_wal_writes())
44            && !version.memtables.is_empty()
45            && region.is_flushable()
46        {
47            info!("Region {} has pending data, waiting for flush", region_id);
48            if self.flush_scheduler.is_flush_requested(region_id) {
49                self.flush_scheduler
50                    .add_ddl_request_to_pending(SenderDdlRequest {
51                        region_id,
52                        sender,
53                        request: DdlRequest::Close(request),
54                    });
55                return;
56            }
57            self.handle_flush_request(
58                region_id,
59                RegionFlushRequest {
60                    reason: Some(RegionFlushReason::Closing),
61                    ..Default::default()
62                },
63                sender,
64            );
65            return;
66        }
67
68        // No close-time flush is required, or the region cannot flush.
69        self.remove_region(region_id).await;
70        info!("Region {} closed, worker: {}", region_id, self.id);
71        sender.send(Ok(0))
72    }
73
74    /// Remove a region and stop all related tasks.
75    pub(crate) async fn remove_region(&mut self, region_id: RegionId) {
76        let Some(region) = self.regions.remove_region(region_id) else {
77            return;
78        };
79        region.stop().await;
80        self.fail_region_stalled_requests_as_not_found(&region_id);
81        self.reject_region_edit_queue_as_not_found(region_id);
82        // Clean flush status.
83        self.flush_scheduler.on_region_closed(region_id);
84        // Clean compaction status.
85        self.compaction_scheduler.on_region_closed(region_id);
86        // clean index build status.
87        self.index_build_scheduler.on_region_closed(region_id).await;
88        self.region_count.dec();
89
90        // Notify the region hook that the region has been closed. The region is
91        // fully stopped and unregistered, but its files/manifest are preserved.
92        // Runs inline; the hook contract requires it to be fast.
93        if let Some(hook) = region.manifest_ctx.hook() {
94            let metadata = region.metadata();
95            hook.on_region_closed(region_id, &metadata).await;
96        }
97    }
98}