mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-13 01:50:40 +02:00
* s3tables: report a delete the filer rejected deleteDirectory discarded DeleteEntryResponse and checked only the transport error, so DeleteTable, DeleteNamespace, DeleteView and DeleteTableBucket answered 200 for a delete the filer refused. Call filer_pb.DoRemove, which reads resp.Error and still treats a missing entry as success. Claude-Session: https://claude.ai/code/session_01BjDWtZsCoZY6x4pdDmGWxU * admin: report a delete the filer rejected The bucket delete, the file browser handlers and the topic retention purger all discarded DeleteEntryResponse, so a delete the filer refused came back as success. Call filer_pb.DoRemove, which reads resp.Error. Claude-Session: https://claude.ai/code/session_01BjDWtZsCoZY6x4pdDmGWxU * credential: report a delete the filer rejected DeleteUser, DeletePolicy and the full-sync cleanup loops discarded DeleteEntryResponse, so a rejected delete answered success and left the credential file in place. The service account path in the same store already read resp.Error; the rest now do too, via filer_pb.DoRemove where not-found is already tolerated. Claude-Session: https://claude.ai/code/session_01BjDWtZsCoZY6x4pdDmGWxU * shell: report a delete the filer rejected remote.configure -delete, remote.cache and the remote metadata sync discarded DeleteEntryResponse, so a rejected delete printed as removed. Call filer_pb.DoRemove, which reads resp.Error. Claude-Session: https://claude.ai/code/session_01BjDWtZsCoZY6x4pdDmGWxU * mq: report a delete the filer rejected The consumer offset group purge and the coordinator assignment delete discarded DeleteEntryResponse. Call filer_pb.DoRemove, which reads resp.Error. Claude-Session: https://claude.ai/code/session_01BjDWtZsCoZY6x4pdDmGWxU * iam: count only the revocation entries the filer actually deleted The expiry sweep discarded DeleteEntryResponse, so a rejected delete was counted as purged and the entry stayed. Call filer_pb.DoRemove, which reads resp.Error, matching the role and provider stores beside it. Claude-Session: https://claude.ai/code/session_01BjDWtZsCoZY6x4pdDmGWxU * mount: fail rmdir when the unary fallback delete was rejected The streaming branch turns DeleteEntryResponse.Error into an error, the unary fallback dropped it, so rmdir of a non-empty directory answered OK off the stream and ENOTEMPTY on it. Surface it in both. Claude-Session: https://claude.ai/code/session_01BjDWtZsCoZY6x4pdDmGWxU * s3tables: fail DeleteTableBucket when the directory delete is refused The handler only failed when both the leaf entry and the directory delete failed, so a refused bucket directory delete still answered 200 with the bucket in place. The directory is the bucket, so it decides; the leaf entry stays best-effort. Claude-Session: https://claude.ai/code/session_01BjDWtZsCoZY6x4pdDmGWxU
78 lines
2.9 KiB
Go
78 lines
2.9 KiB
Go
package mount
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"syscall"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/glog"
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
|
|
)
|
|
|
|
// streamCreateEntry routes a CreateEntryRequest through the streaming mux
|
|
// if available, falling back to a unary gRPC call on transport errors.
|
|
func (wfs *WFS) streamCreateEntry(ctx context.Context, req *filer_pb.CreateEntryRequest) (*filer_pb.CreateEntryResponse, error) {
|
|
if wfs.streamMutate != nil && wfs.streamMutate.IsAvailable() {
|
|
resp, err := wfs.streamMutate.CreateEntry(ctx, req)
|
|
if err == nil || !errors.Is(err, ErrStreamTransport) {
|
|
return resp, err // success or application error — don't retry
|
|
}
|
|
glog.V(1).Infof("streamCreateEntry %s/%s: stream failed, falling back to unary: %v", req.Directory, req.Entry.Name, err)
|
|
}
|
|
var resp *filer_pb.CreateEntryResponse
|
|
err := wfs.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
|
|
var err error
|
|
resp, err = filer_pb.CreateEntryWithResponse(ctx, client, req)
|
|
return err
|
|
})
|
|
return resp, err
|
|
}
|
|
|
|
// streamUpdateEntry routes an UpdateEntryRequest through the streaming mux
|
|
// if available, falling back to a unary gRPC call on transport errors.
|
|
func (wfs *WFS) streamUpdateEntry(ctx context.Context, req *filer_pb.UpdateEntryRequest) (*filer_pb.UpdateEntryResponse, error) {
|
|
if wfs.streamMutate != nil && wfs.streamMutate.IsAvailable() {
|
|
resp, err := wfs.streamMutate.UpdateEntry(ctx, req)
|
|
if err == nil || !errors.Is(err, ErrStreamTransport) {
|
|
return resp, err
|
|
}
|
|
glog.V(1).Infof("streamUpdateEntry %s/%s: stream failed, falling back to unary: %v", req.Directory, req.Entry.Name, err)
|
|
}
|
|
var resp *filer_pb.UpdateEntryResponse
|
|
err := wfs.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
|
|
var err error
|
|
resp, err = client.UpdateEntry(ctx, req)
|
|
return err
|
|
})
|
|
return resp, err
|
|
}
|
|
|
|
// streamDeleteEntry routes a DeleteEntryRequest through the streaming mux
|
|
// if available, falling back to a unary gRPC call on transport errors.
|
|
func (wfs *WFS) streamDeleteEntry(ctx context.Context, req *filer_pb.DeleteEntryRequest) (*filer_pb.DeleteEntryResponse, error) {
|
|
if wfs.streamMutate != nil && wfs.streamMutate.IsAvailable() {
|
|
resp, err := wfs.streamMutate.DeleteEntry(ctx, req)
|
|
if err == nil || !errors.Is(err, ErrStreamTransport) {
|
|
return resp, err
|
|
}
|
|
glog.V(1).Infof("streamDeleteEntry %s/%s: stream failed, falling back to unary: %v", req.Directory, req.Name, err)
|
|
}
|
|
var resp *filer_pb.DeleteEntryResponse
|
|
err := wfs.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
|
|
var err error
|
|
resp, err = client.DeleteEntry(ctx, req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// A refused delete arrives in the response, as the stream branch above.
|
|
if resp.Error != "" {
|
|
return &streamMutateError{msg: resp.Error, errno: syscall.EIO}
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return resp, nil
|
|
}
|