glue-core-0.8.0.0: CHANGELOG.md
# Changelog for glue-core
## 0.8.0.0
### Breaking changes
- Options records for time-dependent combinators take the monad as a type parameter and carry a
`Clock m` defaulting to `systemClock`: `TimeoutOptions m`, `RetryOptions m a`,
`HedgingOptions m`, `BatchingOptions m`, `DogpileOptions m`, `ShutdownOptions m b` and
`PreloadedOptions` (new `preloadingClock` field). Code that updates fields of the defaults keeps
compiling; when an update replaces every field that mentions `m` (for example only the clock),
annotate the default, e.g. `(defaultTimeoutOptions :: TimeoutOptions IO) { timeoutClock = c }` (#13).
- `Glue.CircuitBreaker` counting is fixed and its semantics match the documentation (#5):
- Calls cancelled by an asynchronous exception (timeouts, losing hedges, killed threads) no
longer count as failures; the exception is still rethrown.
- `maxBreakerFailures` is replaced by `breakerFailureThreshold`, which opens on that failure
(the default 3 opens on the third; previously the breaker opened on failure `max + 1`).
- Failures are consecutive by default (a call that answers resets the count; previously the
count never reset), or counted within `breakerFailureWindowMs` when set.
- `resetTimeoutSecs` is replaced by `breakerOpenDurationMs`, how long the breaker stays open,
with millisecond resolution on the monotonic clock rather than wall-clock seconds.
- `CircuitBreakerOptions` and `CircuitBreakerState` take the monad (and options the failure type)
as type parameters.
- `RetryOptions m a failure`: `retryInitialWaitTimeMs` and `retryWaitTimeMultiplier` are replaced
by `retryBackoff` (`ConstantBackoff`, `LinearBackoff` or `ExponentialBackoff`) (#7).
### Additions
- Circuit breaker failure classification with `countsAsFailure`, a phase observer
`onBreakerTransition`, `circuitBreakerPhases`, and a pluggable `BreakerStore` (admit / settle /
read phase, with an atomic half-open trial) so state can be shared between instances, with
`newInMemoryBreakerStore` as the default (#9).
- `circuitBreakerEither` and `circuitBreakerEitherWithStore` for services returning failures as
values (#6).
- Retry decided by the failure (`retryOn`), with a delay ceiling (`retryMaxDelayMs`), full or equal
jitter from an injectable random source (`retryJitter`, `retryRandom`), a deadline after which no
retry starts (`retryRemainingMs`), an attempt observer (`onRetry`) and `retryingServiceEither` (#7).
- `failoverAcross` tries an ordered list of distinct backends, with `advanceOn` deciding which
failures move on and which stop the chain, a choice of reporting the first or last failure, an
indexed variant reporting which backend answered, and `Either` variants (#8).
- `Glue.Race`: a low-level race of attempts (`withRace`, `launch`, `awaitNext` with an optional
timeout, `abandon`, `abandonAll`) for callers driving their own launch decisions. Simultaneous
endings are reported earliest index first, and abandoning waits until the attempt has finished,
reporting how it really ended (#10).
- `hedgeAcross` hedges across distinct backends on top of `Glue.Race`: `admitLaunch` lets the
caller decline a backup without failing the primary, `onAttemptEnded` reports every attempt
(including cancelled losers), an early failure launches the next backend at once, and
`hedgeAcrossEither` supports failures as values. `hedgingService` is now built on it (#10).
- `weightedBy`: a deterministic weighted load balancer. Weights are basis points validated up
front (`Either WeightError`), and each request's backend is chosen from a caller-supplied stable
key by SipHash-2-4 under a `Salt`, so a key reaches the same backend on every instance and across
restarts and different salts split independently. `bucketOf` and `chooseIndex` expose the choice
without calling a backend (#11).
- Keyed concurrency limits: `limitConcurrencyBy` keeps one limit per key and refuses a full key
at once with `Saturated` as a value, releasing on every exit path (including cancellation) and
dropping idle keys. `withSlot` holds a slot for work that outlives the service call, and an
`AdmissionStore` (in-memory by default) is the extension point for limits shared across
instances. `limitConcurrencyEither` and `limitConcurrencyByEither` return refusals as the
caller's failure value (#12).
- `Glue.Periodic`: `periodically` runs a background pass every `periodMs` measured from the end of
the previous pass (optionally immediately and with jitter), reports failed passes without ending
the loop, and returns a handle with `runNow` (queued behind a running pass) and a graceful
`stopPeriodic` (#14).
- `addTimeoutEither` and `gracefulShutdownEither` return their refusal as a value for services
whose failures are values (#6).
## 0.7.0.1
- PVP upper bounds on all dependencies.
## 0.7.0
- Modernised for GHC 9.6 to 9.12, `MonadUnliftIO` throughout, new combinators (hedging,
concurrency limits, load balancing, graceful shutdown) and a Nix flake.