fix(ecstore): complete rename tails after quorum ack (#6489)

This commit is contained in:
Zhengchao An
2026-08-24 14:30:50 +08:00
committed by GitHub
parent 51272d34dd
commit 86e969f63c
7 changed files with 1543 additions and 261 deletions
+65
View File
@@ -842,6 +842,7 @@ mod tests {
use crate::runtime::instance::InstanceContext;
use crate::storage_api_contracts::{
bucket::{BucketOperations as _, BucketOptions, DeleteBucketOptions, MakeBucketOptions, SRBucketDeleteOp},
list::ListOperations as _,
object::{ObjectIO as _, ObjectOperations as _},
};
use crate::store::{ECStore, init_local_disks_with_instance_ctx};
@@ -1664,6 +1665,70 @@ mod tests {
.expect("DeleteBucket must succeed once the client has drained the bucket");
}
#[tokio::test(flavor = "multi_thread")]
#[serial]
async fn bucket_delete_succeeds_after_listing_and_deleting_an_unversioned_overwrite() {
let (disk_paths, ecstore) = setup_bucket_delete_test_env().await;
let bucket = format!("bucket-delete-after-overwrite-{}", Uuid::new_v4().simple());
let object = "object.txt";
ecstore
.make_bucket(&bucket, &MakeBucketOptions::default())
.await
.expect("unversioned bucket should be created");
let mut first_reader = PutObjReader::from_vec(b"version A".to_vec());
let first = ecstore
.put_object(&bucket, object, &mut first_reader, &ObjectOptions::default())
.await
.expect("version A should be written");
let mut second_reader = PutObjReader::from_vec(b"version B".to_vec());
let second = ecstore
.put_object(&bucket, object, &mut second_reader, &ObjectOptions::default())
.await
.expect("version B should overwrite version A");
assert_ne!(first.data_dir, second.data_dir, "the overwrite must publish a new body generation");
let listing = ecstore
.clone()
.list_object_versions(&bucket, "", None, None, None, 1000)
.await
.expect("the overwritten object should remain listable for teardown");
assert_eq!(listing.objects.len(), 1, "an unversioned overwrite should expose one current version");
let current = &listing.objects[0];
assert_eq!(current.name, object);
assert!(current.is_latest, "the listed null version must be current");
assert_eq!(current.version_id, None, "an unversioned object must be exposed as the null version");
assert_eq!(
current.data_dir, second.data_dir,
"listing must expose version B, not the overwritten body"
);
for version in listing.objects {
let version_id = version.version_id.map(|version_id| version_id.to_string());
ecstore
.delete_object(
&bucket,
&version.name,
ObjectOptions {
version_id,
..Default::default()
},
)
.await
.expect("each version returned by teardown listing should be deletable");
}
assert!(
!any_disk_has_object_metadata(&disk_paths, &bucket).await,
"deleting the listed null version must remove every xl.meta"
);
ecstore
.delete_bucket(&bucket, &DeleteBucketOptions::default())
.await
.expect("DeleteBucket should succeed after the listed overwrite is deleted");
}
#[tokio::test]
#[serial]
async fn bucket_delete_default_s3_delete_still_rejects_non_empty_bucket() {