From bd0505eb8000da73391b8b1982b5f84411f85669 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Thu, 5 Nov 2026 23:52:07 -0800 Subject: [PATCH] filer: mark a store transaction independently of the store's own key ArangoDB and FoundationDB keep their transaction under other context keys, so the CreateEntry guard keyed on "tx" missed them. The filer now marks every context returned by BeginTransaction and the guard checks that marker. Co-Authored-By: Claude Opus 5.5 --- weed/filer/filer.go | 20 +++++++++++++------- weed/filer/filer_lazy_remote_test.go | 5 +++-- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/weed/filer/filer.go b/weed/filer/filer.go index 11d5427b4c..3b2531d059 100644 --- a/weed/filer/filer.go +++ b/weed/filer/filer.go @@ -451,8 +451,16 @@ func (fs *Filer) GetMaster(ctx context.Context) pb.ServerAddress { return fs.MasterClient.GetMaster(ctx) } +// storeTransactionKey marks a context inside BeginTransaction, whatever key +// the store keeps its own transaction under. +type storeTransactionKey struct{} + func (f *Filer) BeginTransaction(ctx context.Context) (context.Context, error) { - return f.Store.BeginTransaction(ctx) + txCtx, err := f.Store.BeginTransaction(ctx) + if err != nil { + return txCtx, err + } + return context.WithValue(txCtx, storeTransactionKey{}, true), nil } func (f *Filer) CommitTransaction(ctx context.Context) error { @@ -516,13 +524,11 @@ func (f *Filer) CreateEntry(ctx context.Context, entry *Entry, existing *Entry, oldEntry := existing if oldEntry == nil { var findErr error - // Inside a store transaction the lookup must not run the lazy remote - // fetch: the fetch persists on a second connection, which waits on - // the row lock this transaction holds when it deleted the path (a - // rename onto an existing target). The create writes the path either - // way; reads inside the transaction keep the fetch. + // A lazy fetch here would persist outside the transaction and wait on + // the lock it holds for a target it just deleted; the create writes + // the path either way. findCtx := ctx - if ctx.Value("tx") != nil { + if ctx.Value(storeTransactionKey{}) != nil { findCtx = context.WithValue(ctx, lazyFetchContextKey{}, true) } oldEntry, findErr = f.FindEntry(findCtx, entry.FullPath) diff --git a/weed/filer/filer_lazy_remote_test.go b/weed/filer/filer_lazy_remote_test.go index 7c59b8384a..48e0101859 100644 --- a/weed/filer/filer_lazy_remote_test.go +++ b/weed/filer/filer_lazy_remote_test.go @@ -743,8 +743,9 @@ func TestCreateEntry_SkipsTheLazyFetchInsideAStoreTransaction(t *testing.T) { } f, store := newMountedTestFiler(t, storageType, countingStub, 0) putDirEntry(store, "/buckets/mybucket/dir") - // the key a store transaction travels under (abstract_sql getTxOrDB) - txCtx := context.WithValue(context.Background(), "tx", struct{}{}) + // the stub opens no transaction of its own; the filer marks it anyway + txCtx, err := f.BeginTransaction(context.Background()) + require.NoError(t, err) // a create inside the transaction: its lookup does not consult the // remote, the entry is inserted as given