Skip to main content

datanode/
utils.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_meta::DatanodeId;
18use common_meta::key::datanode_table::DatanodeTableManager;
19use common_meta::key::topic_name::{TopicNameKey, TopicNameManager, TopicNameValue};
20use common_meta::key::topic_region::{
21    ReplayCheckpoint as MetadataReplayCheckpoint, TopicRegionKey, TopicRegionManager,
22    TopicRegionValue,
23};
24use common_meta::kv_backend::KvBackendRef;
25use common_meta::wal_provider::{
26    RegionWalOptions, extract_topic_from_wal_options, serialize_wal_options,
27};
28use futures::TryStreamExt;
29use snafu::ResultExt;
30use store_api::metric_engine_consts::METRIC_ENGINE_NAME;
31use store_api::path_utils::table_dir;
32use store_api::region_request::{PathType, RegionOpenRequest, ReplayCheckpoint};
33use store_api::storage::RegionId;
34use tracing::info;
35
36use crate::error::{GetMetadataSnafu, Result, SerializeWalOptionsSnafu};
37
38/// The requests to open regions.
39pub struct RegionOpenRequests {
40    pub(crate) leader_regions: Vec<(RegionId, RegionOpenRequest)>,
41    #[cfg(feature = "enterprise")]
42    pub(crate) follower_regions: Vec<(RegionId, RegionOpenRequest)>,
43}
44
45impl RegionOpenRequests {
46    /// Splits the request set into leader and follower regions.
47    #[allow(clippy::type_complexity)]
48    pub fn into_parts(
49        self,
50    ) -> (
51        Vec<(RegionId, RegionOpenRequest)>,
52        Vec<(RegionId, RegionOpenRequest)>,
53    ) {
54        let leader_regions = self.leader_regions;
55        #[cfg(feature = "enterprise")]
56        let follower_regions = self.follower_regions;
57        #[cfg(not(feature = "enterprise"))]
58        let follower_regions = Vec::new();
59        (leader_regions, follower_regions)
60    }
61}
62
63fn group_region_by_topic(
64    region_id: RegionId,
65    region_options: &RegionWalOptions,
66    topic_regions: &mut HashMap<String, Vec<RegionId>>,
67) {
68    if let Some(topic) = extract_topic_from_wal_options(region_id, region_options) {
69        topic_regions.entry(topic).or_default().push(region_id);
70    }
71}
72
73fn region_pruned_entry_ids(
74    topic_regions: &HashMap<String, Vec<RegionId>>,
75    topic_name_values: &HashMap<String, TopicNameValue>,
76) -> HashMap<RegionId, u64> {
77    topic_regions
78        .iter()
79        .flat_map(|(topic, region_ids)| {
80            topic_name_values
81                .get(topic)
82                .into_iter()
83                .flat_map(move |value| {
84                    region_ids
85                        .iter()
86                        .map(move |region_id| (*region_id, value.pruned_entry_id))
87                })
88        })
89        .collect()
90}
91
92fn get_replay_checkpoint(
93    region_id: RegionId,
94    topic_region_values: &Option<HashMap<RegionId, TopicRegionValue>>,
95    pruned_entry_id: Option<u64>,
96    is_metric_engine: bool,
97) -> Option<ReplayCheckpoint> {
98    let checkpoint = topic_region_values
99        .as_ref()
100        .and_then(|values| values.get(&region_id))
101        .and_then(|value| value.checkpoint)
102        .map(|checkpoint| {
103            MetadataReplayCheckpoint::new(checkpoint.entry_id, checkpoint.metadata_entry_id)
104        });
105
106    MetadataReplayCheckpoint::merge_with_topic_pruned_entry_id(
107        checkpoint,
108        pruned_entry_id,
109        is_metric_engine,
110    )
111    .map(|checkpoint| ReplayCheckpoint {
112        entry_id: checkpoint.entry_id,
113        metadata_entry_id: checkpoint.metadata_entry_id,
114    })
115}
116
117/// Builds region-open requests from persisted metadata.
118pub async fn build_region_open_requests(
119    node_id: DatanodeId,
120    kv_backend: KvBackendRef,
121) -> Result<RegionOpenRequests> {
122    let datanode_table_manager = DatanodeTableManager::new(kv_backend.clone());
123    let table_values = datanode_table_manager
124        .tables(node_id)
125        .try_collect::<Vec<_>>()
126        .await
127        .context(GetMetadataSnafu)?;
128
129    let topic_region_manager = TopicRegionManager::new(kv_backend.clone());
130    let topic_name_manager = TopicNameManager::new(kv_backend);
131    let mut topic_regions = HashMap::<String, Vec<RegionId>>::new();
132    let mut regions = vec![];
133    #[cfg(feature = "enterprise")]
134    let mut follower_regions = vec![];
135
136    for table_value in table_values {
137        for region_number in table_value.regions {
138            let region_id = RegionId::new(table_value.table_id, region_number);
139            // Augments region options with wal options if a wal options is provided.
140            let mut region_options = table_value.region_info.region_options.clone();
141            serialize_wal_options(
142                &mut region_options,
143                region_id,
144                &table_value.region_info.region_wal_options,
145            )
146            .context(SerializeWalOptionsSnafu { region_id })?;
147            group_region_by_topic(
148                region_id,
149                &table_value.region_info.region_wal_options,
150                &mut topic_regions,
151            );
152
153            regions.push((
154                region_id,
155                table_value.region_info.engine.clone(),
156                table_value.region_info.region_storage_path.clone(),
157                region_options,
158            ));
159        }
160
161        #[cfg(feature = "enterprise")]
162        for region_number in table_value.follower_regions {
163            let region_id = RegionId::new(table_value.table_id, region_number);
164            // Augments region options with wal options if a wal options is provided.
165            let mut region_options = table_value.region_info.region_options.clone();
166            serialize_wal_options(
167                &mut region_options,
168                RegionId::new(table_value.table_id, region_number),
169                &table_value.region_info.region_wal_options,
170            )
171            .context(SerializeWalOptionsSnafu { region_id })?;
172            group_region_by_topic(
173                region_id,
174                &table_value.region_info.region_wal_options,
175                &mut topic_regions,
176            );
177
178            follower_regions.push((
179                RegionId::new(table_value.table_id, region_number),
180                table_value.region_info.engine.clone(),
181                table_value.region_info.region_storage_path.clone(),
182                region_options,
183            ));
184        }
185    }
186
187    let topic_region_values = if !topic_regions.is_empty() {
188        let keys = topic_regions
189            .iter()
190            .flat_map(|(topic, regions)| {
191                regions
192                    .iter()
193                    .map(|region_id| TopicRegionKey::new(*region_id, topic))
194            })
195            .collect::<Vec<_>>();
196        let topic_region_manager = topic_region_manager
197            .batch_get(keys)
198            .await
199            .context(GetMetadataSnafu)?;
200        Some(topic_region_manager)
201    } else {
202        None
203    };
204
205    let topic_name_values = if !topic_regions.is_empty() {
206        let topics = topic_regions
207            .keys()
208            .map(|topic| TopicNameKey::new(topic))
209            .collect::<Vec<_>>();
210        Some(
211            topic_name_manager
212                .batch_get(topics)
213                .await
214                .context(GetMetadataSnafu)?,
215        )
216    } else {
217        None
218    };
219    let region_pruned_entry_ids = topic_name_values
220        .as_ref()
221        .map(|values| region_pruned_entry_ids(&topic_regions, values));
222
223    let mut leader_region_requests = Vec::with_capacity(regions.len());
224    for (region_id, engine, store_path, options) in regions {
225        let table_dir = table_dir(&store_path, region_id.table_id());
226        let pruned_entry_id = region_pruned_entry_ids
227            .as_ref()
228            .and_then(|values| values.get(&region_id).copied());
229        let checkpoint = get_replay_checkpoint(
230            region_id,
231            &topic_region_values,
232            pruned_entry_id,
233            engine == METRIC_ENGINE_NAME,
234        );
235        info!("region_id: {}, checkpoint: {:?}", region_id, checkpoint);
236        leader_region_requests.push((
237            region_id,
238            RegionOpenRequest {
239                engine,
240                table_dir,
241                path_type: PathType::Bare,
242                options,
243                skip_wal_replay: false,
244                checkpoint,
245                requirements: Default::default(),
246            },
247        ));
248    }
249
250    #[cfg(feature = "enterprise")]
251    let follower_region_requests = {
252        let mut follower_region_requests = Vec::with_capacity(follower_regions.len());
253        for (region_id, engine, store_path, options) in follower_regions {
254            let table_dir = table_dir(&store_path, region_id.table_id());
255            follower_region_requests.push((
256                region_id,
257                RegionOpenRequest {
258                    engine,
259                    table_dir,
260                    path_type: PathType::Bare,
261                    options,
262                    skip_wal_replay: true,
263                    checkpoint: None,
264                    requirements: Default::default(),
265                },
266            ));
267        }
268        follower_region_requests
269    };
270
271    Ok(RegionOpenRequests {
272        leader_regions: leader_region_requests,
273        #[cfg(feature = "enterprise")]
274        follower_regions: follower_region_requests,
275    })
276}