Skip to content

Configuration

wiki edited this page Sep 4, 2026 · 1 revision

Configuration

app := rex.New(health.WithHealth(health.NewConfig(
	health.WithCheck(dbCheck),
	health.WithCheckInterval(15*time.Second),
	health.WithHealthRouter(rx.RouterConfig{Addr: "127.0.0.1:9091"}),
)))

health.WithHealth(nil) takes the defaults.

⚠ A non-nil config is used verbatim

There is no field-by-field merge with the defaults. A partial struct literal leaves every field you did not set at its zero value, not its default:

// Wrong: SnapshotTTL 0, CheckInterval 0 (ticker off), Router.Addr "",
// TreatUnknownAs StatusUp only by coincidence of the zero value.
health.WithHealth(&health.Config{LivePath: "/healthz"})

// Right.
health.WithHealth(health.NewConfig(health.WithLivePath("/healthz")))

The merge used to exist and behaved as a trap:

  • Fields it simply forgot were silently ignored — any value the application set was discarded, with no error and no log line.
  • Fields copied unconditionally had the opposite problem: a partial literal zeroed them, so setting one unrelated field turned another off.
  • Zero and "unset" were indistinguishable, so a deliberate zero could not be expressed at all.

Build the config with NewConfig and the With* options. That is the shape that cannot drift out of step with the struct.

Fields

Field Default
LivePath /live liveness endpoint
ReadyPath /ready readiness endpoint
StatusPath /status full status endpoint
AtDefaultAddr false serve on the application router instead of a dedicated one
Router :9091, base /, TLS off the dedicated health listener
SnapshotTTL 5s how long /ready and /status reuse a snapshot
CheckInterval 10s active-check ticker; 0 disables it
StateStoreConfig 5 / 2 / 30s failure, degraded thresholds and window
EnableDependencyGate true attach the per-route gate
TreatUnknownAs StatusUp how the gate treats an unknown dependency
Checks none checks registered at startup

Options

WithLivePath(p) / WithReadyPath(p) / WithStatusPath(p) endpoint paths
WithAtDefaultAddr(bool) use the application router
WithHealthRouter(rx.RouterConfig) configure the dedicated listener
WithSnapshotTTL(d) snapshot cache TTL
WithCheckInterval(d) active-check ticker; 0 disables
WithStateStoreConfig(cfg) failure thresholds and window
WithDependencyGate(bool) enable or disable the gate
WithTreatUnknownAs(status) gate behaviour for unknown dependencies
WithCheck(c) / WithChecks(c…) declare checks

Check options

Passed to health.NewCheck:

Default
WithReadiness(bool) false does it affect /ready
WithCheckMode(mode) Active Active / Passive / OnDemand
WithTimeout(d) bounds one execution
WithCacheTTL(d) 30s Passive only; ignored for OnDemand
WithTags(t…) none for subset execution

A production shape

const (
	DepDatabase = "database"
	DepCache    = "cache"
)

cfg := health.NewConfig(
	// Internal listener only.
	health.WithHealthRouter(rx.RouterConfig{
		Addr:         "127.0.0.1:9091",
		ReadTimeout:  5 * time.Second,
		MaxBodyBytes: 4 << 10,
	}),

	// Poll the critical dependency; let the optional one be checked on demand.
	health.WithCheck(health.NewCheck(DepDatabase, checkDB,
		health.WithReadiness(true),
		health.WithCheckMode(health.CheckModeActive),
		health.WithTimeout(2*time.Second),
	)),
	health.WithCheck(health.NewCheck(DepCache, checkCache,
		health.WithCheckMode(health.CheckModePassive),
		health.WithCacheTTL(30*time.Second),
	)),

	health.WithCheckInterval(10*time.Second),
	health.WithSnapshotTTL(5*time.Second),

	// Refuse rather than guess, if that is your posture.
	health.WithTreatUnknownAs(health.StatusDown),
)

Reading the internals

ext := health.NewHealthExtension(cfg).(*health.HealthExtension)
app := rex.New(rex.WithExtension(ext))

ext.Registry()         // register or execute checks
ext.StateStore()       // report success/failure, read state
ext.SnapshotCache()    // invalidate, retune the TTL
ext.CheckCache()       // passive check results
ext.RouteDepMap()      // route → requirements
ext.MiddlewareConfig() // a base for composing the gate yourself
ext.RegisterCheck(c)   // after OnInitialize has run

These are live after OnInitialize. Before Run they are not yet built.

Clone this wiki locally