Files
s3/weed/util/buffer_pool/sync_pool_test.go
zooqueen dd74d3e6d6 Replace AGPL MinIO fork with Apache-2.0 SeaweedFS fork; rebrand CLI weed->s3
hanzoai/s3 was an AGPL-3.0 MinIO fork whose network-copyleft blocks embedding
and reselling as a managed service. Replace it wholesale with a fork of
SeaweedFS (github.com/seaweedfs/seaweedfs, Apache-2.0, Go) which ships a
built-in S3 gateway + Filer.

- License: AGPL-3.0 -> Apache-2.0 (upstream LICENSE kept verbatim, incl.
  "Copyright 2025 Chris Lu"); NOTICE added crediting SeaweedFS. No AGPL/BUSL/
  SSPL/GPL or enterprise ("ee") files remain.
- Module: go.mod path set to github.com/hanzoai/s3 with a self-replace
  (replace github.com/seaweedfs/seaweedfs => ./) so the ~1800 internal Go files
  keep their upstream import paths with zero churn. Internal identifiers and
  import paths are intentionally NOT renamed.
- CLI rebrand: the binary is built as `s3` (was `weed`); root usage, per-command
  help/usage/flag strings, shell autocomplete program name, and the product name
  in CLI output now read "Hanzo S3" / `s3`. Users never see `weed`.
  (e.g. `s3 server`, `s3 filer`, `s3 master`, `s3 volume`, `s3 s3`, `s3 mount`.)
- Build: `make` (or `go build -o s3 ./weed`) builds the `s3` binary green;
  `cmd/weed-db` and `cmd/weed-sql` also build; `go vet ./weed/command/` clean.
- README/LLM.md rewritten for Hanzo S3; AGENTS.md/CLAUDE.md symlinks preserved.
- Dropped a 33MB prebuilt Mach-O `stress` binary upstream had committed; `.gitignore`
  updated to ignore the `s3` build output.
2026-06-18 23:41:25 -07:00

93 lines
3.3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package buffer_pool
import (
"bytes"
"testing"
)
// TestSyncPoolPutBuffer_DropsOversized is a regression guard for the volume-side
// retention amplifier in https://github.com/seaweedfs/seaweedfs/issues/6541.
//
// volume.PostHandler -> needle.ParseUpload -> bytes.Buffer.ReadFrom grows the
// buffer to chunk size. If we Put that buffer back as-is, sync.Pool keeps it
// at the grown capacity for the rest of the process's lifetime. With
// concurrent UploadPartCopy load that fills the pool with N × chunk-size
// backing arrays that never shrink — exactly the "RSS never recedes" pattern
// reported in the issue.
//
// We verify a Put + Get round trip can never round-trip a buffer larger than
// maxRetainedBufferCap, regardless of how big it grew while in use.
func TestSyncPoolPutBuffer_DropsOversized(t *testing.T) {
// Drain the pool so we start from a deterministic point. sync.Pool may
// still hold cached entries on other Ps, but for the small/big-cap
// distinction below that doesn't matter — we assert an invariant on
// every Get, not the identity of a specific buffer.
for i := 0; i < 64; i++ {
_ = SyncPoolGetBuffer()
}
big := &bytes.Buffer{}
big.Grow(maxRetainedBufferCap * 4) // simulate a large upload buffer
if got := big.Cap(); got <= maxRetainedBufferCap {
t.Fatalf("test setup: big.Cap=%d should exceed threshold %d",
got, maxRetainedBufferCap)
}
SyncPoolPutBuffer(big)
// Any number of Gets must not return a buffer with cap > threshold.
// (If big had been retained, sync.Pool's per-P cache would surface it
// on the very next Get on this goroutine.)
for i := 0; i < 16; i++ {
got := SyncPoolGetBuffer()
if cap := got.Cap(); cap > maxRetainedBufferCap {
t.Fatalf("Get %d returned buffer with cap=%d, exceeds threshold %d "+
"(regression: oversized buffers retained in pool?)",
i, cap, maxRetainedBufferCap)
}
}
}
// TestSyncPoolPutBuffer_KeepsRightSized verifies the cap is one-sided: we
// still pool reasonably-sized buffers so the common case (small uploads,
// header parsing) doesn't pay an alloc per request.
func TestSyncPoolPutBuffer_KeepsRightSized(t *testing.T) {
for i := 0; i < 64; i++ {
_ = SyncPoolGetBuffer()
}
small := &bytes.Buffer{}
small.Grow(maxRetainedBufferCap / 2)
smallCap := small.Cap()
SyncPoolPutBuffer(small)
// We don't assert pointer identity (sync.Pool can hand back any cached
// buffer), but the previously-Put buffer should appear among Gets on
// the same goroutine in the absence of a GC. If our cap-policy ever
// regresses to dropping right-sized buffers, this test starts seeing
// only fresh (cap=0) buffers and fails.
sawPooled := false
for i := 0; i < 8; i++ {
got := SyncPoolGetBuffer()
if got.Cap() == smallCap {
sawPooled = true
break
}
}
if !sawPooled {
t.Fatalf("right-sized buffer (cap=%d) never came back from the pool "+
"(regression: cap policy too aggressive?)", smallCap)
}
}
// TestSyncPoolPutBuffer_NilSafe documents that Put tolerates a nil buffer.
// The volume server defers Put on a buffer obtained via Get, but defensive
// callers in other paths may Put(nil); we should not panic.
func TestSyncPoolPutBuffer_NilSafe(t *testing.T) {
defer func() {
if r := recover(); r != nil {
t.Fatalf("SyncPoolPutBuffer(nil) panicked: %v", r)
}
}()
SyncPoolPutBuffer(nil)
}