From 2f048466b4beeb2b4ebeff1ca3f29e86cd310ef1 Mon Sep 17 00:00:00 2001 From: overtrue Date: Sat, 22 Aug 2026 12:10:41 +0800 Subject: [PATCH] fix(ecstore): distinguish active ILM target cleanup --- .../bucket/lifecycle/tier_delete_journal.rs | 2 +- crates/ecstore/src/core/pools.rs | 13 +++++--- crates/ecstore/src/store/init.rs | 32 +++++++++++++++---- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/crates/ecstore/src/bucket/lifecycle/tier_delete_journal.rs b/crates/ecstore/src/bucket/lifecycle/tier_delete_journal.rs index 4e5ccb2fe..22bc2ca70 100644 --- a/crates/ecstore/src/bucket/lifecycle/tier_delete_journal.rs +++ b/crates/ecstore/src/bucket/lifecycle/tier_delete_journal.rs @@ -439,7 +439,7 @@ async fn process_committed_tier_delete_journal_entry(api: Arc, je: &Jen .record_durable_ilm_decommission_terminal_target_pools(&path, &data) .await .map_err(std::io::Error::other)?; - if !target_pool_indices.is_empty() { + if let Some(target_pool_indices) = target_pool_indices { for target_pool_idx in target_pool_indices { match config_boundary::delete_config(api.pools[target_pool_idx].clone(), &path).await { Ok(()) | Err(Error::ConfigNotFound) => {} diff --git a/crates/ecstore/src/core/pools.rs b/crates/ecstore/src/core/pools.rs index 0ad42d335..3ff9e3d88 100644 --- a/crates/ecstore/src/core/pools.rs +++ b/crates/ecstore/src/core/pools.rs @@ -5328,7 +5328,12 @@ impl ECStore { } } - async fn advance_durable_ilm_decommission_receipts(&self, path: &str, data: &[u8], terminal: bool) -> Result> { + async fn advance_durable_ilm_decommission_receipts( + &self, + path: &str, + data: &[u8], + terminal: bool, + ) -> Result>> { let active_runs = { let pool_meta = self.pool_meta.read().await; pool_meta @@ -5345,7 +5350,7 @@ impl ECStore { .collect::>() }; if active_runs.is_empty() { - return Ok(Vec::new()); + return Ok(None); } let stage = if terminal { "terminal" } else { "progress" }; @@ -5378,7 +5383,7 @@ impl ECStore { ))); } } - Ok(terminal_target_pool_indices) + Ok(Some(terminal_target_pool_indices)) } pub(crate) async fn record_durable_ilm_decommission_progress(&self, path: &str, data: &[u8]) -> Result<()> { @@ -5398,7 +5403,7 @@ impl ECStore { &self, path: &str, data: &[u8], - ) -> Result> { + ) -> Result>> { self.advance_durable_ilm_decommission_receipts(path, data, true).await } diff --git a/crates/ecstore/src/store/init.rs b/crates/ecstore/src/store/init.rs index 20ea839d3..d8a029f37 100644 --- a/crates/ecstore/src/store/init.rs +++ b/crates/ecstore/src/store/init.rs @@ -3284,22 +3284,40 @@ mod tests { let record = validate_durable_ilm_record(&path, &data).expect("tier journal should validate"); let source_zero_receipt = store - .persist_decommission_durable_ilm_receipt_for_test(0, 2, &path, &record, false) - .await - .expect("source pool zero receipt should persist on the target"); - store .persist_decommission_durable_ilm_receipt_for_test(0, 1, &path, &record, false) .await - .expect("source pool zero receipt may reach another active source first"); + .expect("source pool zero receipt should persist on the other active source"); let source_one_receipt = store - .persist_decommission_durable_ilm_receipt_for_test(1, 2, &path, &record, false) + .persist_decommission_durable_ilm_receipt_for_test(1, 0, &path, &record, false) .await - .expect("source pool one receipt should persist on the target"); + .expect("source pool one receipt should persist on the other active source"); assert_ne!( source_zero_receipt, source_one_receipt, "active source runs must have distinct receipt paths" ); + let stats = recover_tier_delete_journal_entries(store.clone(), 100, None) + .await + .expect("cross-source receipts should not remove active source journals"); + assert_eq!((stats.scanned, stats.deleted, stats.failed), (1, 1, 0)); + for pool in &store.pools { + assert_eq!( + com::read_config(pool.clone(), &path) + .await + .expect("cross-source receipts alone must retain every journal copy"), + data + ); + } + + store + .persist_decommission_durable_ilm_receipt_for_test(0, 2, &path, &record, false) + .await + .expect("source pool zero receipt should persist on the target"); + store + .persist_decommission_durable_ilm_receipt_for_test(1, 2, &path, &record, false) + .await + .expect("source pool one receipt should persist on the target"); + let stats = recover_tier_delete_journal_entries(store.clone(), 100, None) .await .expect("multi-source tier journal recovery should complete");