From 12b360f499c146acf99d3476e87226e7b9f7b30d Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 11 Mar 2026 13:49:03 -0700 Subject: [PATCH] fix(s3api): delete bucket directory before collection to prevent inconsistent state Reorder DeleteBucketHandler to remove the bucket directory first, then delete the collection. If collection deletion fails, the bucket is still effectively deleted and can be recreated. Previously, if directory deletion succeeded but collection deletion failed, the bucket was left in an unrecoverable state. --- weed/s3api/s3api_bucket_handlers.go | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/weed/s3api/s3api_bucket_handlers.go b/weed/s3api/s3api_bucket_handlers.go index 3a3e64763..e8a1fd064 100644 --- a/weed/s3api/s3api_bucket_handlers.go +++ b/weed/s3api/s3api_bucket_handlers.go @@ -377,9 +377,18 @@ func (s3a *S3ApiServer) DeleteBucketHandler(w http.ResponseWriter, r *http.Reque } } - err := s3a.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error { + // Delete bucket directory first, then collection. This order ensures that if + // collection deletion fails, the bucket directory is already gone, preventing + // the "collection exists but bucket directory missing" inconsistency that blocks + // bucket recreation. An orphaned collection is harmless and will be cleaned up + // or reused when the bucket is recreated. + err := s3a.rm(s3a.option.BucketsPath, bucket, false, true) + if err != nil { + s3err.WriteErrorResponse(w, r, s3err.ErrInternalError) + return + } - // delete collection + err = s3a.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error { deleteCollectionRequest := &filer_pb.DeleteCollectionRequest{ Collection: s3a.getCollectionName(bucket), } @@ -393,15 +402,9 @@ func (s3a *S3ApiServer) DeleteBucketHandler(w http.ResponseWriter, r *http.Reque }) if err != nil { - s3err.WriteErrorResponse(w, r, s3err.ErrInternalError) - return - } - - err = s3a.rm(s3a.option.BucketsPath, bucket, false, true) - - if err != nil { - s3err.WriteErrorResponse(w, r, s3err.ErrInternalError) - return + // Log but don't fail — the bucket directory is already removed, so the bucket + // is effectively deleted. The orphaned collection will be cleaned up or reused. + glog.Errorf("DeleteBucketHandler: failed to delete collection for bucket %s: %v", bucket, err) } // Clean up bucket-related caches, locks, and metrics after successful deletion