1use std::any::Any;
16
17use common_procedure::Status;
18use common_telemetry::info;
19use serde::{Deserialize, Serialize};
20use snafu::OptionExt;
21use table::metadata::TableId;
22use table::table_name::TableName;
23
24use crate::ddl::DdlContext;
25use crate::ddl::drop_database::cursor::DropDatabaseCursor;
26use crate::ddl::drop_database::{DropDatabaseContext, DropTableTarget, State};
27use crate::ddl::drop_table::executor::DropTableExecutor;
28use crate::ddl::utils::get_region_wal_options;
29use crate::error::{self, Result};
30use crate::key::table_route::TableRouteValue;
31use crate::region_keeper::OperatingRegionGuard;
32use crate::rpc::router::{RegionRoute, operating_leader_regions};
33
34#[derive(Debug, Serialize, Deserialize)]
35pub(crate) struct DropDatabaseExecutor {
36 table_id: TableId,
37 physical_table_id: TableId,
38 table_name: TableName,
39 pub(crate) physical_region_routes: Vec<RegionRoute>,
41 pub(crate) target: DropTableTarget,
42 #[serde(skip)]
43 dropping_regions: Vec<OperatingRegionGuard>,
44}
45
46impl DropDatabaseExecutor {
47 pub fn new(
49 table_id: TableId,
50 physical_table_id: TableId,
51 table_name: TableName,
52 physical_region_routes: Vec<RegionRoute>,
53 target: DropTableTarget,
54 ) -> Self {
55 Self {
56 table_id,
57 physical_table_id,
58 table_name,
59 physical_region_routes,
60 target,
61 dropping_regions: vec![],
62 }
63 }
64}
65
66impl DropDatabaseExecutor {
67 pub(crate) fn register_dropping_regions(&mut self, ddl_ctx: &DdlContext) -> Result<()> {
69 if !self.dropping_regions.is_empty() {
70 return Ok(());
71 }
72 let dropping_regions = operating_leader_regions(&self.physical_region_routes);
73 let mut dropping_region_guards = Vec::with_capacity(dropping_regions.len());
74 for (region_id, datanode_id) in dropping_regions {
75 let guard = ddl_ctx
76 .memory_region_keeper
77 .register(datanode_id, region_id)
78 .context(error::RegionOperatingRaceSnafu {
79 region_id,
80 peer_id: datanode_id,
81 })?;
82 dropping_region_guards.push(guard);
83 }
84 self.dropping_regions = dropping_region_guards;
85 Ok(())
86 }
87}
88
89#[async_trait::async_trait]
90#[typetag::serde]
91impl State for DropDatabaseExecutor {
92 fn recover(&mut self, ddl_ctx: &DdlContext) -> Result<()> {
93 self.register_dropping_regions(ddl_ctx)
94 }
95
96 async fn next(
97 &mut self,
98 ddl_ctx: &DdlContext,
99 ctx: &mut DropDatabaseContext,
100 ) -> Result<(Box<dyn State>, Status)> {
101 self.register_dropping_regions(ddl_ctx)?;
102 let executor = DropTableExecutor::new(self.table_name.clone(), self.table_id, true);
103 if ctx.retrying {
104 info!(
105 "Remapping region routes addresses for retrying drop regions for table_id: {}",
106 self.table_id
107 );
108 let storage = ddl_ctx
109 .table_metadata_manager
110 .table_route_manager()
111 .table_route_storage();
112 storage
115 .remap_region_routes(&mut self.physical_region_routes)
116 .await?;
117 }
118 let table_route_value = TableRouteValue::new(
120 self.table_id,
121 self.physical_table_id,
122 self.physical_region_routes.clone(),
123 );
124
125 let region_wal_options = get_region_wal_options(
127 &ddl_ctx.table_metadata_manager,
128 &table_route_value,
129 self.physical_table_id,
130 )
131 .await?;
132
133 executor
134 .on_destroy_metadata(ddl_ctx, &table_route_value, ®ion_wal_options)
135 .await?;
136 executor.invalidate_table_cache(ddl_ctx).await?;
137 executor
138 .on_drop_regions(
139 &ddl_ctx.node_manager,
140 &ddl_ctx.leader_region_registry,
141 &self.physical_region_routes,
142 true,
143 false,
144 false,
145 )
146 .await?;
147 info!("Table: {}({}) is dropped", self.table_name, self.table_id);
148
149 Ok((
150 Box::new(DropDatabaseCursor::new(self.target)),
151 Status::executing(false),
152 ))
153 }
154
155 fn as_any(&self) -> &dyn Any {
156 self
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use std::sync::Arc;
163
164 use api::region::RegionResponse;
165 use api::v1::region::RegionRequest;
166 use common_catalog::consts::{DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME};
167 use common_error::ext::BoxedError;
168 use common_query::request::QueryRequest;
169 use common_recordbatch::SendableRecordBatchStream;
170 use table::table_name::TableName;
171
172 use crate::ddl::drop_database::cursor::DropDatabaseCursor;
173 use crate::ddl::drop_database::executor::DropDatabaseExecutor;
174 use crate::ddl::drop_database::{DropDatabaseContext, DropTableTarget, State};
175 use crate::ddl::test_util::datanode_handler::DatanodeWatcher;
176 use crate::ddl::test_util::{
177 create_logical_table, create_physical_table, put_datanode_address,
178 };
179 use crate::error::{self, Error, Result};
180 use crate::key::datanode_table::DatanodeTableKey;
181 use crate::peer::Peer;
182 use crate::rpc::router::region_distribution;
183 use crate::test_util::{MockDatanodeHandler, MockDatanodeManager, new_ddl_context};
184
185 #[derive(Clone)]
186 pub struct NaiveDatanodeHandler;
187
188 #[async_trait::async_trait]
189 impl MockDatanodeHandler for NaiveDatanodeHandler {
190 async fn handle(&self, _peer: &Peer, _request: RegionRequest) -> Result<RegionResponse> {
191 Ok(RegionResponse::new(0))
192 }
193
194 async fn handle_query(
195 &self,
196 _peer: &Peer,
197 _request: QueryRequest,
198 ) -> Result<SendableRecordBatchStream> {
199 unreachable!()
200 }
201 }
202
203 #[tokio::test]
204 async fn test_next_with_physical_table() {
205 let node_manager = Arc::new(MockDatanodeManager::new(NaiveDatanodeHandler));
206 let ddl_context = new_ddl_context(node_manager);
207 let physical_table_id = create_physical_table(&ddl_context, "phy").await;
208 let (_, table_route) = ddl_context
209 .table_metadata_manager
210 .table_route_manager()
211 .get_physical_table_route(physical_table_id)
212 .await
213 .unwrap();
214 {
215 let mut state = DropDatabaseExecutor::new(
216 physical_table_id,
217 physical_table_id,
218 TableName::new(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME, "phy"),
219 table_route.region_routes.clone(),
220 DropTableTarget::Physical,
221 );
222 let mut ctx = DropDatabaseContext {
223 catalog: DEFAULT_CATALOG_NAME.to_string(),
224 schema: DEFAULT_SCHEMA_NAME.to_string(),
225 drop_if_exists: false,
226 tables: None,
227 retrying: false,
228 };
229 let (state, status) = state.next(&ddl_context, &mut ctx).await.unwrap();
230 assert!(!status.need_persist());
231 let cursor = state.as_any().downcast_ref::<DropDatabaseCursor>().unwrap();
232 assert_eq!(cursor.target, DropTableTarget::Physical);
233 }
234 let mut ctx = DropDatabaseContext {
236 catalog: DEFAULT_CATALOG_NAME.to_string(),
237 schema: DEFAULT_SCHEMA_NAME.to_string(),
238 drop_if_exists: false,
239 tables: None,
240 retrying: false,
241 };
242 let mut state = DropDatabaseExecutor::new(
243 physical_table_id,
244 physical_table_id,
245 TableName::new(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME, "phy"),
246 table_route.region_routes.clone(),
247 DropTableTarget::Physical,
248 );
249 let (state, status) = state.next(&ddl_context, &mut ctx).await.unwrap();
250 assert!(!status.need_persist());
251 let cursor = state.as_any().downcast_ref::<DropDatabaseCursor>().unwrap();
252 assert_eq!(cursor.target, DropTableTarget::Physical);
253 }
254
255 #[tokio::test]
256 async fn test_next_logical_table() {
257 let node_manager = Arc::new(MockDatanodeManager::new(NaiveDatanodeHandler));
258 let ddl_context = new_ddl_context(node_manager);
259 let physical_table_id = create_physical_table(&ddl_context, "phy").await;
260 create_logical_table(ddl_context.clone(), physical_table_id, "metric").await;
261 let logical_table_id = physical_table_id + 1;
262 let (_, table_route) = ddl_context
263 .table_metadata_manager
264 .table_route_manager()
265 .get_physical_table_route(logical_table_id)
266 .await
267 .unwrap();
268 {
269 let mut state = DropDatabaseExecutor::new(
270 logical_table_id,
271 physical_table_id,
272 TableName::new(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME, "metric"),
273 table_route.region_routes.clone(),
274 DropTableTarget::Logical,
275 );
276 let mut ctx = DropDatabaseContext {
277 catalog: DEFAULT_CATALOG_NAME.to_string(),
278 schema: DEFAULT_SCHEMA_NAME.to_string(),
279 drop_if_exists: false,
280 tables: None,
281 retrying: false,
282 };
283 let (state, status) = state.next(&ddl_context, &mut ctx).await.unwrap();
284 assert!(!status.need_persist());
285 let cursor = state.as_any().downcast_ref::<DropDatabaseCursor>().unwrap();
286 assert_eq!(cursor.target, DropTableTarget::Logical);
287 }
288 let mut ctx = DropDatabaseContext {
290 catalog: DEFAULT_CATALOG_NAME.to_string(),
291 schema: DEFAULT_SCHEMA_NAME.to_string(),
292 drop_if_exists: false,
293 tables: None,
294 retrying: false,
295 };
296 let mut state = DropDatabaseExecutor::new(
297 logical_table_id,
298 physical_table_id,
299 TableName::new(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME, "metric"),
300 table_route.region_routes,
301 DropTableTarget::Logical,
302 );
303 let (state, status) = state.next(&ddl_context, &mut ctx).await.unwrap();
304 assert!(!status.need_persist());
305 let cursor = state.as_any().downcast_ref::<DropDatabaseCursor>().unwrap();
306 assert_eq!(cursor.target, DropTableTarget::Logical);
307 ddl_context
309 .table_metadata_manager
310 .table_info_manager()
311 .get(physical_table_id)
312 .await
313 .unwrap()
314 .unwrap();
315 let table_route = ddl_context
317 .table_metadata_manager
318 .table_route_manager()
319 .table_route_storage()
320 .get(physical_table_id)
321 .await
322 .unwrap()
323 .unwrap();
324 let region_routes = table_route.region_routes().unwrap();
325 for datanode_id in region_distribution(region_routes).into_keys() {
326 ddl_context
327 .table_metadata_manager
328 .datanode_table_manager()
329 .get(&DatanodeTableKey::new(datanode_id, physical_table_id))
330 .await
331 .unwrap()
332 .unwrap();
333 }
334 }
335
336 #[derive(Clone)]
337 pub struct RetryErrorDatanodeHandler;
338
339 #[async_trait::async_trait]
340 impl MockDatanodeHandler for RetryErrorDatanodeHandler {
341 async fn handle(&self, _peer: &Peer, _request: RegionRequest) -> Result<RegionResponse> {
342 Err(Error::RetryLater {
343 source: BoxedError::new(
344 error::UnexpectedSnafu {
345 err_msg: "retry later",
346 }
347 .build(),
348 ),
349 clean_poisons: false,
350 })
351 }
352
353 async fn handle_query(
354 &self,
355 _peer: &Peer,
356 _request: QueryRequest,
357 ) -> Result<SendableRecordBatchStream> {
358 unreachable!()
359 }
360 }
361
362 #[tokio::test]
363 async fn test_next_retryable_err() {
364 let node_manager = Arc::new(MockDatanodeManager::new(RetryErrorDatanodeHandler));
365 let ddl_context = new_ddl_context(node_manager);
366 let physical_table_id = create_physical_table(&ddl_context, "phy").await;
367 let (_, table_route) = ddl_context
368 .table_metadata_manager
369 .table_route_manager()
370 .get_physical_table_route(physical_table_id)
371 .await
372 .unwrap();
373 let mut state = DropDatabaseExecutor::new(
374 physical_table_id,
375 physical_table_id,
376 TableName::new(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME, "phy"),
377 table_route.region_routes,
378 DropTableTarget::Physical,
379 );
380 let mut ctx = DropDatabaseContext {
381 catalog: DEFAULT_CATALOG_NAME.to_string(),
382 schema: DEFAULT_SCHEMA_NAME.to_string(),
383 drop_if_exists: false,
384 tables: None,
385 retrying: false,
386 };
387 let err = state.next(&ddl_context, &mut ctx).await.unwrap_err();
388 assert!(err.is_retry_later());
389 }
390
391 #[tokio::test]
392 async fn test_on_recovery() {
393 let node_manager = Arc::new(MockDatanodeManager::new(NaiveDatanodeHandler));
394 let ddl_context = new_ddl_context(node_manager);
395 let physical_table_id = create_physical_table(&ddl_context, "phy").await;
396 let (_, table_route) = ddl_context
397 .table_metadata_manager
398 .table_route_manager()
399 .get_physical_table_route(physical_table_id)
400 .await
401 .unwrap();
402 {
403 let mut state = DropDatabaseExecutor::new(
404 physical_table_id,
405 physical_table_id,
406 TableName::new(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME, "phy"),
407 table_route.region_routes.clone(),
408 DropTableTarget::Physical,
409 );
410 let mut ctx = DropDatabaseContext {
411 catalog: DEFAULT_CATALOG_NAME.to_string(),
412 schema: DEFAULT_SCHEMA_NAME.to_string(),
413 drop_if_exists: false,
414 tables: None,
415 retrying: false,
416 };
417 state.recover(&ddl_context).unwrap();
418 assert_eq!(state.dropping_regions.len(), 1);
419 let (state, status) = state.next(&ddl_context, &mut ctx).await.unwrap();
420 assert!(!status.need_persist());
421 let cursor = state.as_any().downcast_ref::<DropDatabaseCursor>().unwrap();
422 assert_eq!(cursor.target, DropTableTarget::Physical);
423 }
424 }
425
426 #[tokio::test]
427 async fn test_next_remaps_addresses_when_retrying() {
428 let (tx, mut rx) = tokio::sync::mpsc::channel(8);
429 let node_manager = Arc::new(MockDatanodeManager::new(DatanodeWatcher::new(tx)));
430 let ddl_context = new_ddl_context(node_manager);
431 let physical_table_id = create_physical_table(&ddl_context, "phy").await;
432 let (_, table_route) = ddl_context
433 .table_metadata_manager
434 .table_route_manager()
435 .get_physical_table_route(physical_table_id)
436 .await
437 .unwrap();
438
439 let mut state = DropDatabaseExecutor::new(
440 physical_table_id,
441 physical_table_id,
442 TableName::new(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME, "phy"),
443 table_route.region_routes,
444 DropTableTarget::Physical,
445 );
446 state.physical_region_routes[0]
447 .leader_peer
448 .as_mut()
449 .unwrap()
450 .addr = "old-addr".to_string();
451 let mut ctx = DropDatabaseContext {
452 catalog: DEFAULT_CATALOG_NAME.to_string(),
453 schema: DEFAULT_SCHEMA_NAME.to_string(),
454 drop_if_exists: false,
455 tables: None,
456 retrying: true,
457 };
458
459 put_datanode_address(&ddl_context, 0, "new-addr").await;
460
461 state.next(&ddl_context, &mut ctx).await.unwrap();
462
463 let (peer, _) = rx.try_recv().unwrap();
464 assert_eq!(peer.addr, "new-addr");
465 }
466}