Files
seaweedfs/weed/server/filer_grpc_server_create_lock_test.go
T
Chris LuandGitHub f46b2a1925 Stop the filer test helpers from pinning gigabytes of log buffers (#10560)
* log buffer: wake the interval loop on shutdown instead of sleeping through it

loopInterval parked in time.Sleep(flushInterval) and only re-checked
IsStopping when it woke, so a buffer shut down early kept both loop
goroutines - and the PreviousBufferCount+1 slabs of BufferSize they
reach - alive for up to a full interval afterwards. Select on shutdownCh
against a ticker instead, and give the loops a WaitGroup so a test can
observe that they exit.

* test: release the filers the server tests build

Every helper here left its filer's meta log buffer running, so each test
pinned PreviousBufferCount+1 buffers of BufferSize for the rest of the
run: ~3.5GB of live heap across the package, which overruns the address
space on linux/386 and kills the 32-bit job with an out-of-memory throw.

Thread the test through the helpers so the buffer is shut down on
cleanup, and shut the subscribe harness's filer down outright - its
deletion loop keeps the whole filer reachable otherwise. That harness
quiesces its flush path first, since Filer.Shutdown closes the store a
flush still in flight would write through.
2026-08-04 11:38:38 -07:00

69 lines
2.0 KiB
Go

package weed_server
import (
"context"
"sync"
"sync/atomic"
"testing"
"time"
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
"github.com/seaweedfs/seaweedfs/weed/util"
)
// Concurrent OExcl creates for the same path must yield exactly one winner. The
// filer's CreateEntry is a FindEntry-then-Insert; without the per-path lock both
// racers observe "not found" and both insert. The exclusive entry lock makes the
// check-then-act atomic so the losers see ErrEntryAlreadyExists.
func TestCreateEntryOExclSerialized(t *testing.T) {
store := newRenameTestStore()
store.findDelay = 5 * time.Millisecond
f := newRenameTestFiler(t, store)
f.DirBucketsPath = "/buckets"
fs := &FilerServer{
filer: f,
option: &FilerOption{},
entryLockTable: util.NewLockTable[util.FullPath](),
}
const racers = 8
var success, alreadyExists, unexpected int32
var wg sync.WaitGroup
start := make(chan struct{})
for i := 0; i < racers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
<-start
resp, err := fs.CreateEntry(context.Background(), &filer_pb.CreateEntryRequest{
Directory: "/test",
OExcl: true,
SkipCheckParentDirectory: true,
Entry: &filer_pb.Entry{
Name: "obj",
Attributes: &filer_pb.FuseAttributes{Mtime: 1700000000, FileMode: 0644, Inode: 1},
},
})
switch {
case err != nil:
atomic.AddInt32(&unexpected, 1)
case resp.Error == "":
atomic.AddInt32(&success, 1)
case resp.ErrorCode == filer_pb.FilerError_ENTRY_ALREADY_EXISTS:
atomic.AddInt32(&alreadyExists, 1)
default:
atomic.AddInt32(&unexpected, 1)
}
}()
}
close(start)
wg.Wait()
// Exactly one winner; every loser fails with ENTRY_ALREADY_EXISTS and nothing
// else, so an unrelated failure can't masquerade as a passing test.
if success != 1 || alreadyExists != racers-1 || unexpected != 0 {
t.Fatalf("winners=%d already_exists=%d unexpected=%d (racers=%d)", success, alreadyExists, unexpected, racers)
}
}