1use 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
38pub 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 #[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(®ion_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
117pub 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 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 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(®ion_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}