Skip to main content

metric_engine/engine/
catchup.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::collections::HashMap;
16
17use common_error::ext::BoxedError;
18use snafu::{OptionExt, ResultExt};
19use store_api::region_engine::{BatchResponses, RegionEngine};
20use store_api::region_request::{RegionCatchupRequest, ReplayCheckpoint};
21use store_api::storage::RegionId;
22
23use crate::engine::MetricEngineInner;
24use crate::error::{BatchCatchupMitoRegionSnafu, PhysicalRegionNotFoundSnafu, Result};
25use crate::utils;
26
27impl MetricEngineInner {
28    pub async fn handle_batch_catchup_requests(
29        &self,
30        parallelism: usize,
31        requests: Vec<(RegionId, RegionCatchupRequest)>,
32    ) -> Result<BatchResponses> {
33        let mut all_requests = Vec::with_capacity(requests.len() * 2);
34        let mut physical_region_options_list = Vec::with_capacity(requests.len());
35
36        for (region_id, req) in requests {
37            let metadata_region_id = utils::to_metadata_region_id(region_id);
38            let data_region_id = utils::to_data_region_id(region_id);
39
40            let physical_region_options = *self
41                .state
42                .read()
43                .unwrap()
44                .physical_region_states()
45                .get(&data_region_id)
46                .context(PhysicalRegionNotFoundSnafu {
47                    region_id: data_region_id,
48                })?
49                .options();
50            physical_region_options_list.push((data_region_id, physical_region_options));
51            all_requests.push((
52                metadata_region_id,
53                RegionCatchupRequest {
54                    set_writable: req.set_writable,
55                    entry_id: req.metadata_entry_id,
56                    metadata_entry_id: None,
57                    checkpoint: req.checkpoint.map(|c| ReplayCheckpoint {
58                        entry_id: c.metadata_entry_id.unwrap_or_default(),
59                        metadata_entry_id: None,
60                    }),
61                },
62            ));
63            all_requests.push((
64                data_region_id,
65                RegionCatchupRequest {
66                    set_writable: req.set_writable,
67                    entry_id: req.entry_id,
68                    metadata_entry_id: None,
69                    checkpoint: req.checkpoint.map(|c| ReplayCheckpoint {
70                        entry_id: c.entry_id,
71                        metadata_entry_id: None,
72                    }),
73                },
74            ));
75        }
76
77        let mut results = self
78            .mito
79            .handle_batch_catchup_requests(parallelism, all_requests)
80            .await
81            .context(BatchCatchupMitoRegionSnafu {})?
82            .into_iter()
83            .collect::<HashMap<_, _>>();
84
85        let mut responses = Vec::with_capacity(physical_region_options_list.len());
86        for (physical_region_id, physical_region_options) in physical_region_options_list {
87            let metadata_region_id = utils::to_metadata_region_id(physical_region_id);
88            let data_region_id = utils::to_data_region_id(physical_region_id);
89            let metadata_region_result = results.remove(&metadata_region_id);
90            let data_region_result = results.remove(&data_region_id);
91
92            // Pass the optional `metadata_region_result` and `data_region_result` to
93            // `recover_physical_region_with_results`. This function handles errors for each
94            // catchup physical region request, allowing the process to continue with the
95            // remaining regions even if some requests fail.
96            let response = self
97                .recover_physical_region_with_results(
98                    metadata_region_result,
99                    data_region_result,
100                    physical_region_id,
101                    physical_region_options,
102                    // Note: We intentionally don’t close the region if recovery fails.
103                    // Closing it here might confuse the region server since it links RegionIds to Engines.
104                    // If recovery didn’t succeed, the region should stay open.
105                    false,
106                )
107                .await
108                .map_err(BoxedError::new);
109            responses.push((physical_region_id, response));
110        }
111
112        Ok(responses)
113    }
114}