-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Liveness, readiness, dependency state tracking, circuit breakers and per-route dependency gating for Rex.
go get github.com/kryovyx/rextension-healthimport (
"github.com/kryovyx/rex"
health "github.com/kryovyx/rextension-health"
)
app := rex.New(
health.WithHealth(health.NewConfig(
health.WithCheck(health.NewCheck("database", checkDatabase,
health.WithReadiness(true),
health.WithCheckMode(health.CheckModeActive),
)),
)),
)Out of the box that gives you /live, /ready and /status on a dedicated
listener at :9091, a check running every 10 seconds, and a dependency
state store the rest of your code can report into.
Endpoints — /live, /ready, /status, on their own port so they stay
answerable when the application listener is saturated.
Health Checks — named checks with timeouts, tags, readiness flags, and three execution modes (active polling, passive with caching, on-demand).
Dependency State — a store of UP / DEGRADED / DOWN per
dependency, fed by checks, by your own ReportSuccess/ReportFailure calls, or
by a wrapped HTTP client.
The Dependency Gate — routes declare what they need; requests to a route whose hard dependency is down are refused with a 503 before the handler runs.
Plus Circuit Breakers, which stop hammering a dependency that is already failing.
// 1. A check that reports into the state store.
dbCheck := health.NewCheck("database",
func(ctx context.Context, r rx.Resolver) *health.CheckResult {
var db *sql.DB
if err := r.Resolve(&db); err != nil {
return health.NewCheckResult(health.StatusDown, "unresolvable", 0)
}
start := time.Now()
if err := db.PingContext(ctx); err != nil {
return health.NewCheckResult(health.StatusDown, "ping failed", time.Since(start))
}
return health.NewCheckResult(health.StatusUp, "", time.Since(start))
},
health.WithReadiness(true),
health.WithCheckMode(health.CheckModeActive),
health.WithTimeout(2*time.Second),
)
app := rex.New(health.WithHealth(health.NewConfig(health.WithCheck(dbCheck))))
// 2. A route that refuses to serve without the database.
app.RegisterRoute(health.NewRouteWithDeps("GET", "/users/{id}", getUser,
health.NewHardRequirement("database"),
))$ curl :9091/ready
{"status":"UP","checks":{"database":{"status":"UP","duration":1200000,…}}}
The gate serves when it does not know. TreatUnknownAs defaults to
StatusUp. Availability-gating that fails closed 503s every request for the
first check interval after every boot — a rolling deploy then fails its own
readiness probes and rolls back. What makes that safe is the synchronous check
pass in OnStart: every registered check runs once before the listeners bind,
so by the time a request can arrive the states are real. See
The Dependency Gate.
Configuration is used verbatim. A non-nil Config is not merged with the
defaults field by field. Build it with NewConfig and the With* options — a
partial struct literal leaves the rest at their zero values, not their
defaults. See Configuration.
Checks are declared in the config, not registered before Run. The registry
does not exist until the extension's OnInitialize runs, so reaching into the
container for it beforehand resolves nothing. Use WithCheck / WithChecks.
The client never learns which dependency failed. The 503's detail is deliberately generic; the identifier and its state go to the log. Naming it would map your internal service topology for anyone probing endpoints during an outage.
rextension-health — health, readiness and dependency gating for Rex · MIT · © 2026 Kryovyx