aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorchrislu <chris.lu@gmail.com>2025-09-09 08:22:12 -0700
committerchrislu <chris.lu@gmail.com>2025-09-09 08:22:12 -0700
commit311dfa9a2375e0fda8793167e06a023161383730 (patch)
tree5a00bf52949d420d0770b203e66f6d2f94358546
parentb3a401d9f9da02fa45f96c838caf48773d5ea177 (diff)
downloadseaweedfs-311dfa9a2375e0fda8793167e06a023161383730.tar.xz
seaweedfs-311dfa9a2375e0fda8793167e06a023161383730.zip
do not retry if error is NotFound
-rw-r--r--weed/s3api/filer_util.go8
-rw-r--r--weed/s3api/s3api_bucket_handlers.go31
2 files changed, 39 insertions, 0 deletions
diff --git a/weed/s3api/filer_util.go b/weed/s3api/filer_util.go
index 9dd9a684e..ad16a3b48 100644
--- a/weed/s3api/filer_util.go
+++ b/weed/s3api/filer_util.go
@@ -65,10 +65,18 @@ func doDeleteEntry(client filer_pb.SeaweedFilerClient, parentDirectoryPath strin
glog.V(1).Infof("delete entry %v/%v: %v", parentDirectoryPath, entryName, request)
if resp, err := client.DeleteEntry(context.Background(), request); err != nil {
+ // Treat NotFound as success for idempotency
+ if strings.Contains(err.Error(), filer_pb.ErrNotFound.Error()) {
+ return nil
+ }
glog.V(0).Infof("delete entry %v: %v", request, err)
return fmt.Errorf("delete entry %s/%s: %v", parentDirectoryPath, entryName, err)
} else {
if resp.Error != "" {
+ // Treat NotFound as success for idempotency
+ if strings.Contains(resp.Error, filer_pb.ErrNotFound.Error()) {
+ return nil
+ }
return fmt.Errorf("delete entry %s/%s: %v", parentDirectoryPath, entryName, resp.Error)
}
}
diff --git a/weed/s3api/s3api_bucket_handlers.go b/weed/s3api/s3api_bucket_handlers.go
index f68aaa3a0..36d630f95 100644
--- a/weed/s3api/s3api_bucket_handlers.go
+++ b/weed/s3api/s3api_bucket_handlers.go
@@ -108,6 +108,27 @@ func (s3a *S3ApiServer) PutBucketHandler(w http.ResponseWriter, r *http.Request)
return
}
+ // Idempotency: if bucket already exists and is owned by the requester, return success.
+ identityId := r.Header.Get(s3_constants.AmzIdentityId)
+ if existingEntry, err := s3a.getEntry(s3a.option.BucketsPath, bucket); err == nil && existingEntry != nil && existingEntry.IsDirectory {
+ if owner, ok := existingEntry.Extended[s3_constants.AmzIdentityId]; ok {
+ if string(owner) == identityId || identityId == "" {
+ w.Header().Set("Location", "/"+bucket)
+ writeSuccessResponseEmpty(w, r)
+ return
+ }
+ } else {
+ // No owner recorded; treat as success for idempotency
+ w.Header().Set("Location", "/"+bucket)
+ writeSuccessResponseEmpty(w, r)
+ return
+ }
+ } else if err != nil && !errors.Is(err, filer_pb.ErrNotFound) {
+ // Unexpected error fetching existing entry
+ s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
+ return
+ }
+
// avoid duplicated buckets
errCode := s3err.ErrNone
if err := s3a.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
@@ -192,6 +213,16 @@ func (s3a *S3ApiServer) DeleteBucketHandler(w http.ResponseWriter, r *http.Reque
bucket, _ := s3_constants.GetBucketAndObject(r)
glog.V(3).Infof("DeleteBucketHandler %s", bucket)
+ // Idempotency: if the bucket doesn't exist, return NoContent (success)
+ if entry, err := s3a.getEntry(s3a.option.BucketsPath, bucket); err != nil || entry == nil {
+ if errors.Is(err, filer_pb.ErrNotFound) || entry == nil {
+ s3err.WriteEmptyResponse(w, r, http.StatusNoContent)
+ return
+ }
+ s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
+ return
+ }
+
if err := s3a.checkBucket(r, bucket); err != s3err.ErrNone {
s3err.WriteErrorResponse(w, r, err)
return