metric_engine/engine/
drop.rs1use common_telemetry::{debug, info};
18use snafu::ResultExt;
19use store_api::region_engine::RegionEngine;
20use store_api::region_request::{AffectedRows, RegionDropRequest, RegionRequest};
21use store_api::storage::RegionId;
22
23use crate::engine::MetricEngineInner;
24use crate::error::{
25 CloseMitoRegionSnafu, LogicalRegionNotFoundSnafu, PhysicalRegionBusySnafu, Result,
26};
27use crate::metrics::PHYSICAL_REGION_COUNT;
28use crate::utils;
29
30impl MetricEngineInner {
31 pub async fn drop_region(
32 &self,
33 region_id: RegionId,
34 req: RegionDropRequest,
35 ) -> Result<AffectedRows> {
36 let data_region_id = utils::to_data_region_id(region_id);
37 let fast_path = req.fast_path;
38 let force = req.force;
39
40 let (is_physical_region, is_physical_region_busy) = {
42 if let Some(state) = self
43 .state
44 .read()
45 .unwrap()
46 .physical_region_states()
47 .get(&data_region_id)
48 {
49 debug!(
50 "Physical region {} is busy, there are still some logical regions: {:?}",
51 data_region_id,
52 state
53 .logical_regions()
54 .iter()
55 .map(|id| id.to_string())
56 .collect::<Vec<_>>()
57 );
58 (true, !state.logical_regions().is_empty())
59 } else {
60 (false, true)
62 }
63 };
64
65 if is_physical_region {
66 if is_physical_region_busy && !force {
68 return Err(PhysicalRegionBusySnafu {
70 region_id: data_region_id,
71 }
72 .build());
73 }
74 if is_physical_region_busy && force {
75 info!("Dropping physical region {} with force", data_region_id);
76 }
77 return self
78 .drop_physical_region(data_region_id, req.partial_drop)
79 .await;
80 }
81
82 if fast_path {
83 self.state
88 .write()
89 .unwrap()
90 .remove_logical_region(region_id)?;
91
92 Ok(0)
93 } else {
94 let metadata_region_id = self
95 .state
96 .read()
97 .unwrap()
98 .logical_regions()
99 .get(®ion_id)
100 .copied();
101 if let Some(metadata_region_id) = metadata_region_id {
102 self.drop_logical_region(region_id, metadata_region_id)
103 .await
104 } else {
105 Err(LogicalRegionNotFoundSnafu { region_id }.build())
106 }
107 }
108 }
109
110 async fn drop_physical_region(
111 &self,
112 region_id: RegionId,
113 partial_drop: bool,
114 ) -> Result<AffectedRows> {
115 let data_region_id = utils::to_data_region_id(region_id);
116 let metadata_region_id = utils::to_metadata_region_id(region_id);
117
118 self.mito
122 .handle_request(
123 data_region_id,
124 RegionRequest::Drop(RegionDropRequest {
125 fast_path: false,
126 force: false,
127 partial_drop,
128 }),
129 )
130 .await
131 .with_context(|_| CloseMitoRegionSnafu { region_id })?;
132 self.mito
133 .handle_request(
134 metadata_region_id,
135 RegionRequest::Drop(RegionDropRequest {
136 fast_path: false,
137 force: false,
138 partial_drop,
139 }),
140 )
141 .await
142 .with_context(|_| CloseMitoRegionSnafu { region_id })?;
143
144 PHYSICAL_REGION_COUNT.dec();
145
146 self.state
148 .write()
149 .unwrap()
150 .remove_physical_region(data_region_id)?;
151
152 Ok(0)
153 }
154
155 async fn drop_logical_region(
156 &self,
157 logical_region_id: RegionId,
158 physical_region_id: RegionId,
159 ) -> Result<AffectedRows> {
160 let _write_guard = self
163 .metadata_region
164 .write_lock_logical_region(logical_region_id)
165 .await?;
166 let result = self
168 .metadata_region
169 .remove_logical_region(physical_region_id, logical_region_id)
170 .await;
171 self.state
174 .write()
175 .unwrap()
176 .invalid_logical_regions_cache([logical_region_id]);
177 result?;
178
179 self.state
181 .write()
182 .unwrap()
183 .remove_logical_region(logical_region_id)?;
184
185 Ok(0)
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use api::v1::Rows;
192 use common_wal::config::object_store::{ObjectStoreWalConfig, STANDALONE_GENERATION};
193 use common_wal::options::{ObjectStoreWalOptions, WAL_OPTIONS_KEY, WalOptions};
194 use log_store::ObjectStoreLogStore;
195 use mito2::config::MitoConfig;
196 use mito2::test_util::TestEnv as MitoTestEnv;
197 use object_store::ObjectStore;
198 use object_store::services::Memory;
199 use store_api::logstore::LogStore;
200 use store_api::region_engine::RegionEngine;
201 use store_api::region_request::{RegionDropRequest, RegionPutRequest, RegionRequest};
202 use store_api::storage::RegionId;
203
204 use crate::test_util::{TestEnv, build_rows, row_schema_with_tags};
205 use crate::utils::{to_data_region_id, to_metadata_region_id};
206
207 fn drop_request() -> RegionRequest {
208 RegionRequest::Drop(RegionDropRequest {
209 fast_path: false,
210 force: false,
211 partial_drop: false,
212 })
213 }
214
215 #[derive(Clone, Copy, PartialEq)]
217 enum ReleaseFailure {
218 Data,
220 Both,
222 Conflict,
225 }
226
227 #[tokio::test]
228 async fn test_drop_physical_region_whose_wal_release_fails() {
229 for failure in [
230 ReleaseFailure::Data,
231 ReleaseFailure::Both,
232 ReleaseFailure::Conflict,
233 ] {
234 let object_store = ObjectStore::new(Memory::default()).unwrap();
235 let config = ObjectStoreWalConfig::default();
236 let open_wal = || {
237 ObjectStoreLogStore::try_new(
238 object_store.clone(),
239 &config,
240 0,
241 STANDALONE_GENERATION,
242 )
243 };
244 let wal = open_wal().await.unwrap();
245 let mut mito_env = MitoTestEnv::with_prefix("metric-drop-failed-release").await;
246 let mito = mito_env
247 .create_engine_with_log_store(MitoConfig::default(), wal.clone())
248 .await;
249 let env = TestEnv::with_mito_engine(mito_env, mito);
250 let region_id = RegionId::new(1, 1);
251 let wal_options = WalOptions::ObjectStore(ObjectStoreWalOptions::new(
252 "wal/datanodes/0/epochs/0".to_string(),
253 ));
254 env.create_physical_region(
255 region_id,
256 "test_metric_table",
257 vec![(
258 WAL_OPTIONS_KEY.to_string(),
259 serde_json::to_string(&wal_options).unwrap(),
260 )],
261 )
262 .await;
263 let logical_region_id = RegionId::new(1024, 1);
266 env.create_logical_region(region_id, logical_region_id)
267 .await;
268 let metric = env.metric();
269 metric
270 .handle_request(
271 logical_region_id,
272 RegionRequest::Put(RegionPutRequest {
273 skip_wal: false,
274 rows: Rows {
275 schema: row_schema_with_tags(&["job"]),
276 rows: build_rows(1, 10),
277 },
278 hint: None,
279 partition_expr_version: None,
280 }),
281 )
282 .await
283 .unwrap();
284 metric
285 .handle_request(logical_region_id, drop_request())
286 .await
287 .unwrap();
288
289 match failure {
291 ReleaseFailure::Data => wal.fail_next_create_after_write(),
292 ReleaseFailure::Both => wal.fail_creates(),
293 ReleaseFailure::Conflict => {
294 let objects = "wal/datanodes/0/epochs/0/objects/";
297 let last = object_store
298 .list(objects)
299 .await
300 .unwrap()
301 .iter()
302 .filter_map(|entry| entry.name().strip_suffix(".wal")?.parse::<u64>().ok())
303 .max()
304 .unwrap();
305 let path = |object_seq: u64| format!("{objects}{object_seq:020}.wal");
306 let copy = object_store.read(&path(last)).await.unwrap();
307 object_store.write(&path(last + 1), copy).await.unwrap();
308 }
309 }
310 metric
311 .handle_request(region_id, drop_request())
312 .await
313 .unwrap();
314 assert!(!env.mito().is_region_exists(to_data_region_id(region_id)));
315 assert!(
316 !env.mito()
317 .is_region_exists(to_metadata_region_id(region_id))
318 );
319 assert!(
320 !metric
321 .inner
322 .state
323 .read()
324 .unwrap()
325 .exist_physical_region(to_data_region_id(region_id))
326 );
327
328 if failure == ReleaseFailure::Conflict {
332 assert!(wal.list_namespaces().await.is_err());
333 continue;
334 }
335 wal.allow_creates();
336 wal.stop().await.unwrap();
337 let wal = open_wal().await.unwrap();
338 wal.wait_for_garbage_collection().await;
339 assert!(wal.list_namespaces().await.unwrap().is_empty());
340 wal.stop().await.unwrap();
341 }
342 }
343}