meta_srv/procedure/drain_datanode/
utils.rs1use std::collections::BTreeSet;
16
17use common_meta::DatanodeId;
18use common_meta::key::TableMetadataManagerRef;
19use common_meta::key::table_route::TableRouteValue;
20use common_meta::peer::Peer;
21use common_meta::rpc::router::region_distribution;
22use common_time::util::current_time_millis;
23use futures::TryStreamExt;
24use snafu::ResultExt;
25use store_api::storage::RegionId;
26
27use crate::error::{self, Result};
28use crate::procedure::drain_datanode::{Context, DrainDatanodePlan};
29use crate::selector::common;
30
31pub(crate) async fn build_datanode_plan(
37 table_metadata_manager: &TableMetadataManagerRef,
38 node_id: DatanodeId,
39) -> Result<DrainDatanodePlan> {
40 let mut leader_regions = BTreeSet::new();
41 let mut follower_regions = BTreeSet::new();
42
43 let mut tables = table_metadata_manager
44 .datanode_table_manager()
45 .tables(node_id);
46 while let Some(value) = tables
47 .try_next()
48 .await
49 .context(error::TableMetadataManagerSnafu)?
50 {
51 let Some(table_route) = table_metadata_manager
52 .table_route_manager()
53 .table_route_storage()
54 .get(value.table_id)
55 .await
56 .context(error::TableMetadataManagerSnafu)?
57 else {
58 continue;
59 };
60 let TableRouteValue::Physical(table_route) = table_route else {
61 continue;
62 };
63
64 let mut distribution = region_distribution(&table_route.region_routes);
65 let Some(roles) = distribution.remove(&node_id) else {
66 continue;
67 };
68 leader_regions.extend(
69 roles
70 .leader_regions
71 .into_iter()
72 .map(|number| RegionId::new(value.table_id, number)),
73 );
74 follower_regions.extend(
75 roles
76 .follower_regions
77 .into_iter()
78 .map(|number| RegionId::new(value.table_id, number)),
79 );
80 }
81
82 Ok(DrainDatanodePlan {
83 leader_regions: leader_regions.into_iter().collect(),
84 follower_regions: follower_regions.into_iter().collect(),
85 })
86}
87
88pub(crate) async fn leads_region(
93 table_metadata_manager: &TableMetadataManagerRef,
94 node_id: DatanodeId,
95 region_id: RegionId,
96) -> Result<bool> {
97 let Some(table_route) = table_metadata_manager
98 .table_route_manager()
99 .table_route_storage()
100 .get(region_id.table_id())
101 .await
102 .context(error::TableMetadataManagerSnafu)?
103 else {
104 return Ok(false);
105 };
106 let TableRouteValue::Physical(table_route) = table_route else {
107 return Ok(false);
108 };
109 let Some(region_route) = table_route
110 .region_routes
111 .iter()
112 .find(|region_route| region_route.region.id == region_id)
113 else {
114 return Ok(false);
115 };
116
117 Ok(region_route.leader_peer.as_ref().map(|peer| peer.id) == Some(node_id))
118}
119
120pub(crate) fn ensure_drain_deadline(ctx: &Context) -> Result<()> {
122 if current_time_millis() < ctx.persistent_ctx.deadline_ms {
123 return Ok(());
124 }
125
126 let remaining_leader_regions = ctx
127 .volatile_ctx
128 .plan
129 .as_ref()
130 .map_or(0, |plan| plan.leader_regions.len());
131 error::ExceededDeadlineSnafu {
132 operation: format!(
133 "Drain datanode {}, migrated: {}, remaining leader regions: {}",
134 ctx.persistent_ctx.node_id, ctx.volatile_ctx.migrated, remaining_leader_regions
135 ),
136 }
137 .fail()
138}
139
140pub(crate) async fn select_migration_target(
147 ctx: &Context,
148 region_id: RegionId,
149 follower_peers: &[Peer],
150) -> Result<Peer> {
151 let node_id = ctx.persistent_ctx.node_id;
152 let target_node_id = ctx.persistent_ctx.target_node_id;
153 let target = common::active_schedulable_datanodes(&ctx.selector_ctx, None)
154 .await?
155 .into_iter()
156 .map(|datanode| datanode.peer)
157 .filter(|peer| peer.id != node_id)
158 .filter(|peer| target_node_id.is_none_or(|target| peer.id == target))
159 .filter(|peer| !follower_peers.iter().any(|follower| follower.id == peer.id))
160 .min_by_key(|peer| peer.id);
161
162 match target {
163 Some(peer) => Ok(peer),
164 None => error::DrainDatanodeAbortSnafu {
165 node_id,
166 failed_regions: vec![region_id],
167 }
168 .fail(),
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use common_meta::peer::Peer;
175 use store_api::storage::RegionId;
176
177 use crate::error;
178 use crate::key::NodeSchedulingRole;
179 use crate::procedure::drain_datanode::test_util::{
180 TestEnv, follower_region, leader_region, new_persistent_context,
181 };
182 use crate::procedure::drain_datanode::{DrainDatanodeStrategy, utils};
183
184 #[tokio::test]
185 async fn test_build_datanode_plan() {
186 let env = TestEnv::new().await;
187 let first = RegionId::new(1024, 1);
188 let second = RegionId::new(1024, 2);
189 env.create_table(vec![leader_region(first, 1), follower_region(second, 2, 1)])
190 .await;
191
192 let plan = utils::build_datanode_plan(env.table_metadata_manager(), 1)
193 .await
194 .unwrap();
195
196 assert_eq!(vec![first], plan.leader_regions);
197 assert_eq!(vec![second], plan.follower_regions);
198 }
199
200 #[tokio::test]
201 async fn test_build_datanode_plan_of_empty_datanode() {
202 let env = TestEnv::new().await;
203
204 let plan = utils::build_datanode_plan(env.table_metadata_manager(), 1)
205 .await
206 .unwrap();
207
208 assert!(plan.leader_regions.is_empty());
209 assert!(plan.follower_regions.is_empty());
210 }
211
212 #[tokio::test]
213 async fn test_build_datanode_plan_reads_the_table_route() {
214 let env = TestEnv::new().await;
215 let region_id = RegionId::new(1024, 1);
216 env.create_table(vec![follower_region(region_id, 2, 1)])
219 .await;
220
221 let plan = utils::build_datanode_plan(env.table_metadata_manager(), 1)
222 .await
223 .unwrap();
224
225 assert!(plan.leader_regions.is_empty());
226 assert_eq!(vec![region_id], plan.follower_regions);
227 }
228
229 #[tokio::test]
230 async fn test_select_migration_target() {
231 let env = TestEnv::new().await;
232 let region_id = RegionId::new(1024, 1);
233
234 let mut ctx = env.context(new_persistent_context(
235 None,
236 DrainDatanodeStrategy::FailFast,
237 ));
238 let peer = utils::select_migration_target(&ctx, region_id, &[])
239 .await
240 .unwrap();
241 assert_eq!(2, peer.id);
242
243 let peer = utils::select_migration_target(&ctx, region_id, &[Peer::empty(2)])
245 .await
246 .unwrap();
247 assert_eq!(3, peer.id);
248 assert_eq!("dn3:3001", peer.addr);
249
250 ctx.persistent_ctx.target_node_id = Some(2);
252 let peer = utils::select_migration_target(&ctx, region_id, &[])
253 .await
254 .unwrap();
255 assert_eq!(2, peer.id);
256 assert_eq!("dn2:3001", peer.addr);
257
258 ctx.persistent_ctx.target_node_id = Some(2);
261 let error = utils::select_migration_target(&ctx, region_id, &[Peer::empty(2)])
262 .await
263 .unwrap_err();
264 assert!(matches!(error, error::Error::DrainDatanodeAbort { .. }));
265
266 ctx.persistent_ctx.target_node_id = Some(1);
267 let error = utils::select_migration_target(&ctx, region_id, &[])
268 .await
269 .unwrap_err();
270 assert!(matches!(error, error::Error::DrainDatanodeAbort { .. }));
271
272 env.node_scheduling_manager
273 .mark_unschedulable(NodeSchedulingRole::Datanode, 3)
274 .await
275 .unwrap();
276 ctx.persistent_ctx.target_node_id = Some(3);
277 let error = utils::select_migration_target(&ctx, region_id, &[])
278 .await
279 .unwrap_err();
280 assert!(matches!(error, error::Error::DrainDatanodeAbort { .. }));
281 }
282}