The Open->HalfOpen promotion used time.Now/time.Since directly, forcing tests
to use real time.Sleep and diverging from the project's clock convention. Add
an unexported withClock option (default clock.System) and replace the real
sleeps in tests with mock-clock Advance, making the transitions deterministic
and the package faster.
- Deduplicate sentinel errors: httpx.ErrNoHealthy, ErrCircuitOpen, and
ErrRetryExhausted are now aliases to the canonical sub-package values
so errors.Is works across package boundaries
- Retry transport returns ErrRetryExhausted only when all attempts are
actually exhausted, not on early policy exit
- Balancer: pre-parse endpoint URLs at construction, replace req.Clone
with cheap shallow struct copy to avoid per-request allocations
- Circuit breaker: Load before LoadOrStore to avoid allocating a Breaker
on every request for known hosts
- Health checker: drain response body before close for connection reuse,
probe endpoints concurrently, run initial probe synchronously in Start
- Client: add Close() to shut down health checker goroutine, propagate
URL resolution errors instead of silently discarding them
- MockClock: fix lock ordering in Reset (clock.mu before t.mu), fix
timer slice compaction to avoid backing-array aliasing, extract
fireExpired to deduplicate Advance/Set
Implements circuit breaker as a RoundTripper middleware:
- Closed → Open after consecutive failure threshold
- Open → HalfOpen after configurable duration
- HalfOpen → Closed on success, back to Open on failure
- Per-host tracking via sync.Map for independent endpoint isolation