You are working on `git.codelab.vc/pkg/dbx`, a Go PostgreSQL cluster library built on pgx/v5. ## Architecture - Cluster manages master + replicas with method-based routing (no SQL parsing) - Write ops (Exec, Query, QueryRow, Begin, BeginTx, CopyFrom, SendBatch) → master - Read ops (ReadQuery, ReadQueryRow) → replicas with master fallback - Retry with exponential backoff + jitter, iterates nodes then backs off - Round-robin balancer skips unhealthy nodes - Background health checker pings all nodes on interval - RunTx — panic-safe transaction wrapper (recover → rollback → re-panic) - InjectQuerier/ExtractQuerier — context-based Querier for service layers ## Package structure - `dbx` (root) — Cluster, Node, Balancer, retry, health, errors, tx, config, options - `dbx.go` — interfaces: Querier, DB, Logger, MetricsHook - `cluster.go` — Cluster routing and query execution - `node.go` — Node wrapping pgxpool.Pool with health state - `balancer.go` — Balancer interface + RoundRobinBalancer - `retry.go` — retrier with backoff and node fallback - `health.go` — background health checker goroutine - `tx.go` — RunTx, RunTxOptions, InjectQuerier, ExtractQuerier - `errors.go` — IsRetryable, IsConnectionError, IsConstraintViolation, PgErrorCode - `config.go` — Config, NodeConfig, PoolConfig, RetryConfig, HealthCheckConfig - `options.go` — functional options (WithLogger, WithMetrics, WithRetry, WithHealthCheck, WithSlowQueryThreshold, WithTracer) - `slog.go` — SlogLogger adapting *slog.Logger to dbx.Logger - `scan.go` — Collect[T], CollectOne[T] generic row scan helpers - `stats.go` — PoolStats aggregate pool statistics via Cluster.Stats() - `dbxtest/` — test helpers: NewTestCluster, TestLogger, RunInTx ## Code conventions - Struct-based Config with defaults() method for zero-value defaults - Functional options (Option func(*Config)) used via ApplyOptions - stdlib only testing — no testify, no gomock - Thread safety with atomic.Bool (Node.healthy, Cluster.closed) - dbxtest.NewTestCluster skips on unreachable DB, auto-closes via t.Cleanup - Sentinel errors: ErrNoHealthyNode, ErrClusterClosed, ErrRetryExhausted - retryError multi-unwrap for errors.Is compatibility ## When writing new code - New node type → add to Cluster struct, Config, connect in NewCluster, add to `all` for health checking - New balancer → implement Balancer interface, check IsHealthy(), return nil if no suitable node - New retry logic → provide RetryConfig.RetryableErrors or extend IsRetryable() - New metrics hook → add field to MetricsHook, nil-check before calling - Close() is required — leaking a Cluster leaks goroutines and connections - No SQL parsing — routing is method-based, Exec with SELECT still goes to master ## Commands ```bash go build ./... # compile go test ./... # test go test -race ./... # test with race detector go vet ./... # static analysis ```