filer: keep a renamed entry's old object when the path lookup fails

The deferred delete must only run once the lookup shows no remote-only
entry at the old path. A failed lookup shows nothing, and an extra
object loses nothing.
This commit is contained in:
Alex Russell committed 2026-10-10 21:15:21 +02:00
1 parent 590624908b
commit eabffd9294
3 files changed
+22 -4

No files matched your search

+12 -4
View File
@@ -2,6 +2,7 @@ package weed_server
import (
"context"
"errors"
"fmt"
"maps"
"path/filepath"
@@ -159,11 +160,18 @@ func (event metadataEvent) notify(f *filer.Filer, ctx context.Context, signature
if event.deleteOldRemote {
// a child path is not locked by its directory's rename, so another
// rename may have put a remote-only entry at the old key meanwhile;
// the store is asked directly, a lazy fetch would revive the old key
if current, err := f.Store.FindEntry(ctx, event.oldEntry.FullPath); err == nil && current.IsInRemoteOnly() {
// the store is asked directly, a lazy fetch would revive the old key.
// When in doubt the object stays: an extra object loses nothing.
current, findErr := f.Store.FindEntry(ctx, event.oldEntry.FullPath)
switch {
case findErr != nil && !errors.Is(findErr, filer_pb.ErrNotFound):
glog.WarningfCtx(ctx, "keep remote object of renamed %s: look up its old path: %v", event.oldEntry.FullPath, findErr)
case findErr == nil && current.IsInRemoteOnly():
glog.V(0).InfofCtx(ctx, "keep remote object of renamed %s: a remote-only entry has taken its path", event.oldEntry.FullPath)
} else if err := f.DeleteFromRemote(ctx, event.oldEntry); err != nil {
glog.WarningfCtx(ctx, "delete remote object of renamed %s: %v", event.oldEntry.FullPath, err)
default:
if err := f.DeleteFromRemote(ctx, event.oldEntry); err != nil {
glog.WarningfCtx(ctx, "delete remote object of renamed %s: %v", event.oldEntry.FullPath, err)
}
}
}
f.NotifyUpdateEvent(ctx, event.oldEntry, event.newEntry, event.deleteChunks, false, signatures)
@@ -304,7 +304,13 @@ func TestDeferredOldKeyDeleteKeepsAKeyRetakenSince(t *testing.T) {
event.notify(server.filer, context.Background(), nil)
assert.Empty(t, client.recorded())
// a lookup that fails says nothing about the path
delete(store.entries, "/buckets/b/src/a.jpg")
store.findErr = errors.New("store unavailable")
event.notify(server.filer, context.Background(), nil)
assert.Empty(t, client.recorded())
store.findErr = nil
event.notify(server.filer, context.Background(), nil)
assert.Equal(t, []string{"delete origin/src/a.jpg"}, client.recorded())
}
@@ -31,6 +31,7 @@ type renameTestStore struct {
deleteErr error
listErr error // simulates a transient store/RPC failure from a directory listing
findDelay time.Duration // optional: widen check-then-act windows in tests
findErr error // simulates a failing entry lookup
}
func newRenameTestStore() *renameTestStore {
@@ -77,6 +78,9 @@ func (s *renameTestStore) FindEntry(_ context.Context, p util.FullPath) (*filer.
s.mu.Lock()
defer s.mu.Unlock()
s.findCalls[string(p)]++
if s.findErr != nil {
return nil, s.findErr
}
entry, found := s.entries[string(p)]
if !found {
return nil, filer_pb.ErrNotFound