- handler/static.go: add sync.RWMutex to StaticHandler; protect Handle
with RLock and all setters with Lock to prevent data races
- http2/server.go: delete empty connection slice keys from pool map to
prevent memory leak under high client churn
- loadbalance/slow_start.go: recreate stopCh in Start() to support
Start-Stop-Start cycles; guard double-close in Stop()
- resolver/resolver.go: recreate stopCh in Start() to support restart
- logging/logging.go: save *os.File handles from getOutput so Close()
actually closes log files; exclude os.Stdout/os.Stderr from closing
- ssl/session_tickets.go: protect started/rotateTimer access in
scheduleRotation with mu; support Start-Stop-Start cycles
- ssl/ssl.go: cache parsed default certificate to avoid re-parsing on
every TLS handshake for OCSP stapling
- Verify Least Time picks faster target consistently
- Verify Sticky fallback when target becomes unhealthy
- Test cookie encoding and session persistence
- Add least_time and sticky to valid algorithms list
- Add LeastTimeConfig and StickyConfig structures
- Update default config generation with new options
- Add configuration validation for new fields
- Add sync.Once to prevent double close of stopCh in Stop()
- Add nil fallback guard in NewStickySession (defaults to RoundRobin)
- Add atomic.Bool to make Start() idempotent
- Add tests for double Stop() and nil fallback scenarios
- Fix Select to check if cookie is expired before routing
- Add TestStickySession_ExpiredCookie test
- Expired cookies now trigger fallback + new cookie set
- Encode cookie as base64(target_url + | + timestamp) per spec
- Use cookie value (not targetURL) for shard key and session map keys
- Add missing sticky.Start() calls in tests
- Fix time precision in cookie encode/decode tests
- Add atomic EWMA Stats field to Target
- Implement LeastTime balancer with header_time and last_byte metrics
- Support Select and SelectExcluding with zero-lock design
- Add ResponseTimeRecorder interface for proxy integration
- Zero-lock atomic EWMA implementation using fixed-point arithmetic
- Supports header_time and last_byte_time tracking
- Concurrent-safe with CAS retry loop
- errcheck: check type assertions from sync.Pool.Get() in
loadbalance/balancer.go and matcher/radix.go
- errcheck: check type assertion from list.Element.Value in
resolver/resolver.go evictLRULocked
- revive: add doc comment for exported ReleaseMatchResult function
SelectByKey and SelectExcludingByKey previously had a RLock→RUnlock→
rebuildCircle(Lock)→RLock pattern when the hash ring was empty. Under
cold-start concurrency, multiple goroutines could trigger simultaneous
rebuild attempts.
Add atomic.Bool 'rebuilt' flag with ensureRebuilt() check before any
RLock acquisition:
- Fast path: atomic load returns true → skip rebuild, proceed to RLock
- Cold start: first caller rebuilds and sets flag, subsequent callers
see the flag and skip rebuild
- Rebuild() explicitly resets the flag for explicit ring invalidation
Eliminates the RLock→Unlock→Lock→RLock transition entirely. The ring
is guaranteed ready before RLock is acquired.
filterHealthy() allocated 2 slices (available + backups) per call.
filterHealthyAndExclude() allocated 3 (adds excludeSet map).
IPHash allocated fnv.New64a() object per call.
All triggered on every request's LB selection.
Changes:
- Add filterContext struct holding reusable buffers, managed by sync.Pool
- Replace filterHealthy → filterInto (writes into pooled buffers)
- Replace filterHealthyAndExclude → filterIntoExcluding (pooled buffers)
- Add inline fnvHash64a() to avoid fnv.New64a() heap allocation
- Update all 6 balancer algorithms (RoundRobin, WeightedRoundRobin,
LeastConnections, IPHash, Random, ConsistentHash) to use pooled
filterContext via acquire/release pattern
- ConsistentHash.SelectExcludingByKey also uses pool for targetSet
- Remove buildExcludeSet (merged into filterIntoExcluding)
Result: allocs/op reduced from 2-3 to 0-1 on all LB Select paths.
Remove sync.Mutex from Target's failure tracking hot path:
- failCount int64 → atomic.Int64
- failedUntil int64 → atomic.Int64
- IsAvailable(): Load() instead of Lock/Unlock, eliminating mutex
from every per-target check in every LB Select() call
- RecordFailure(): Add(1) + Store() instead of Lock/Unlock
- RecordSuccess(): Store(0) instead of Lock/Unlock
This removes the only mutex acquisition in the load balancing
selection path. Every Select() call iterates targets and calls
IsAvailable() on each — previously acquiring failMu per target
when MaxFails > 0. Now fully lock-free.
Add t.Parallel() to 110 test functions across 3 test files:
- internal/loadbalance/balancer_test.go (42 tests)
- internal/config/validate_test.go (21 tests)
- internal/server/status_test.go (47 tests)
This reduces total test time from ~3 minutes to ~34 seconds (5.4x faster).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Remove unused benchmark/tools package
- Make ValidAlgorithms private (validAlgorithms) in loadbalance
- Remove dead code (_ = result) in lua/api_socket_tcp.go
- Fix code formatting with goimports
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Apply modern Go patterns across the codebase:
- Replace `interface{}` with `any` (Go 1.18+)
- Use `for range n` instead of `for i := 0; i < n; i++` (Go 1.22+)
- Replace `sort.Slice` with `slices.Sort` from slices package
- Simplify sync.WaitGroup patterns with errgroup where appropriate
- Add Makefile targets for modernize analyzer
Total: 84 files updated, net reduction of 79 lines
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- ConsistentHash: reuse main hash ring in SelectExcludingByKey instead of
rebuilding per call, reducing memory allocation from 369KB to 1.8KB (99.5%)
- RateLimiter: replace single RWMutex with 16-segment sharded locks to
reduce lock contention in high-concurrency scenarios
- TLS SessionTickets: add warning log when KeyFile is empty to alert
users about session invalidation after restart
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
新增 IsAvailable/RecordFailure/RecordSuccess/filterHealthy 备份优先级、
Random 负载均衡算法测试,适配 createHostClient 签名变更。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Target 新增 MaxConns/MaxFails/FailTimeout/Backup/Down/ProxyURI 字段,
实现 IsAvailable/RecordFailure/RecordSuccess 软失败机制,
filterHealthy 支持备份服务器优先级选择,
新增 random(Power of Two Choices)负载均衡算法。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- 所有 *_bench_test.go 文件从 for i := 0; i < b.N; i++ 改为 for b.Loop()
- 部分测试文件从 for i := 0; i < N; ... 改为 for range N 或 for i := range N
- 涵盖模块: cache, handler, http2, http3, loadbalance, logging, lua,
middleware/accesslog, middleware/bodylimit, middleware/rewrite,
middleware/security, netutil, resolver, server, ssl, stream
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Conflict: sendfile.go (!linux build tag) was incorrectly modified to
include linuxSendfile and getSocketFd functions which already exist
in sendfile_linux.go.
Resolution: Keep HEAD version (simple fallback returning ENOTSUP) as
Linux implementation is properly separated in sendfile_linux.go.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>