fix(ecstore): fence deletes against decommission commits (#6363)

* fix(ecstore): fence deletes against decommission commits

* fix(ecstore): preserve decommission target write locks

* fix(ecstore): preserve delete markers during source cleanup

* fix(ecstore): route batch delete markers to active pools

* fix(ecstore): preserve batch delete pool errors

* fix(ecstore): retain source-set lock during cleanup

* test(ecstore): exercise decommission delete fences

* test(ecstore): finish decommission delete fence scenario

* fix(ecstore): reuse fixed fence for reverse decommission

* fix(ecstore): fence decommission commit loss

* fix(ecstore): annotate batch delete fallback

* test(ecstore): fix decommission fence fixtures

* fix(ecstore): unblock decommission delete fences

* fix(ecstore): preserve distributed decommission set locks

* fix(ecstore): match decommission lock backend domain

* test(ecstore): align decommission fence barriers

* fix(ecstore): satisfy delete fence lint checks

* fix(rebalance): preserve access-denied delete errors
This commit is contained in:
Zhengchao An
2026-08-23 00:40:38 +08:00
committed by GitHub
parent 3ddf1a81ac
commit 7c1a76dfd9
13 changed files with 2606 additions and 134 deletions
+28 -3
View File
@@ -3673,6 +3673,9 @@ impl ECStore {
)
.await?;
let source_cleanup_mutation_fence = self
.acquire_decommission_source_cleanup_fence(bucket.as_str(), entry.name.as_str(), set.as_ref())
.await?;
let cleanup_result = data_movement::cleanup_source_entry_if_unchanged(
set.clone(),
bucket.as_str(),
@@ -3684,6 +3687,7 @@ impl ECStore {
lifecycle_guard: bucket_incarnation_fence
.as_ref()
.and_then(|guard| guard.namespace_lock_guard()),
object_mutation_fence: Some(&source_cleanup_mutation_fence),
},
"decommission",
)
@@ -3785,6 +3789,22 @@ impl ECStore {
Ok(())
}
#[cfg(test)]
pub(crate) async fn decommission_entry_for_test(
self: &Arc<Self>,
idx: usize,
entry: MetaCacheEntry,
bucket: String,
set: Arc<SetDisks>,
) -> Result<()> {
let worker_permit = Arc::new(Semaphore::new(1))
.acquire_owned()
.await
.map_err(|err| Error::other(format!("decommission test worker permit acquire failed: {err}")))?;
self.decommission_entry(CancellationToken::new(), idx, entry, bucket, set, worker_permit, None, None, None, None)
.await
}
#[tracing::instrument(skip(self, rx))]
async fn decommission_pool(
self: &Arc<Self>,
@@ -4821,15 +4841,20 @@ impl ECStore {
) -> Result<()> {
warn!("decommission_object: start {} {}", &bucket, &rd.object_info.name);
let object_name = rd.object_info.name.clone();
let result = data_movement::migrate_object(
let mut migration = tokio::task::JoinSet::new();
migration.spawn(data_movement::migrate_decommission_object(
self,
pool_idx,
bucket.clone(),
rd,
expected_bucket_incarnation_id,
"decommission_object",
)
.await;
));
let result = migration
.join_next()
.await
.ok_or_else(|| Error::other("decommission migration task was not started"))?
.map_err(|err| Error::other(format!("decommission migration task join error: {err}")))?;
if result.is_ok() {
warn!("decommission_object: migrated {} {}", &bucket, &object_name);
}