go-tcp-proxy-pool-v2
The implementation is a Go 1.26 TCP proxy (go-tcp-proxy-pool-v2) composed of four modules:
pool.go) — Least-loaded schedulingBackendPool holds a slice of Backend structs, each tracking its active connection count via atomic.Int64 and health via atomic.Bool. LeastLoaded() acquires a read lock, scans all healthy backends, and returns the one with the fewest active connections.
func (p *BackendPool) LeastLoaded() *Backend {
p.mu.RLock()
defer p.mu.RUnlock()
var best *Backend
bestConns := int64(1<<63 - 1)
for _, b := range p.backends {
if !b.health.Load() { continue }
if c := b.conns.Load(); c < bestConns {
bestConns, best = c, b
}
}
return best
}
Add()/Remove() acquire a write lock and modify the slice; Remove() also sets health to false immediately so no new connections are dispatched.
health.go) — TCP dial every 5sHealthChecker runs a goroutine with a time.Ticker at 5-second intervals. Each tick dials every backend with a 2-second timeout. Backends that fail are marked unhealthy; backends that recover are marked healthy again. Log messages report state transitions.
func (hc *HealthChecker) check(addr string) bool {
conn, err := net.DialTimeout("tcp", addr, healthCheckTimeout)
if err != nil { return false }
conn.Close()
return true
}
proxy.go) — Accept loop, SIGHUP, graceful shutdownBACKENDS environment variable (comma-separated host:port list), computes a diff against the current pool, adds new backends, and removes missing ones. The proxy continues running.wg.Wait() blocks until every active proxyConn goroutine finishes. Only then does doneCh close, signalling full shutdown.backend.conns via defer when the connection terminates, ensuring counts stay accurate.config.go) — Backend sourceBackends are read from BACKENDS env var on startup and during SIGHUP reload. Falls back to <ip-address>:9001 for development.
| Requirement | Mechanism |
|---|---|
| Least-loaded assignment | atomic.Int64 counters + read-locked scan |
| Health check (5s) | time.Ticker + net.DialTimeout with 2s timeout |
| Remove dead backends | Health check sets health to false; Remove() removes from slice |
| Add new backends at runtime | SIGHUP → diff → Add() |
| Graceful shutdown | Close listener → sync.WaitGroup.Wait() → close doneCh |
| No dropped active connections | Shutdown blocks until every proxy goroutine completes |
All 8 tests pass: ``` === RUN TestLeastLoadedAssignment — PASS === RUN TestHealthCheck — PASS === RUN TestAddBackend — PASS === RUN TestRemoveBackend — PASS === RUN TestSIGHUPReload — PASS === RUN TestGracefulShutdown — PASS === RUN TestFullProxyEcho — PASS === RUN TestNoHealthyBackend — PASS ``` ### Edge cases tested | Test | What it verifies | |---|---| | `TestLeastLoadedAssignment` | With two backends at equal load, returns the first. After incrementing one, returns the other. | | `TestHealthCheck` | A dead backend (port 19999) is marked unhealthy; only the live backend appears in `LeastLoaded()`. | | `TestAddBackend` | Adding a backend to an empty pool makes it available immediately. | | `TestRemoveBackend` | After removal, `LeastLoaded()` never returns the removed address even after 10 attempts. | | `TestSIGHUPReload` | Sets `BACKENDS` env var to two addresses, starts proxy with one, sends SIGHUP, verifies the second is now in the pool. | | `TestGracefulShutdown` | Opens a connection, sends data, triggers SIGTERM, **reads the echo response** — proves active connections are not dropped. | | `TestFullProxyEcho` | End-to-end: client → proxy → backend echo server → response received. | | `TestNoHealthyBackend` | When all backends are unhealthy, connections are accepted then closed by the proxy (graceful rejection). | ---
{"model": "claude-3.5-sonnet", "problem_class": "go-tcp-proxy-pool-v2", "result": "passed", "tests": 8}