Health checks
Reference Relayer.HealthChecks and register its three checks in the application:
using Relayer.HealthChecks;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRelayer(relayer =>
{
// Declare transports, routes and consumers here.
});
builder.Services.AddRelayerHealthChecks();
var app = builder.Build();
app.MapHealthChecks("/health/ready");
app.Run();
The call returns the standard IHealthChecksBuilder. It works before or after AddRelayer and registers
relayer.transport, relayer.consumer and relayer.outbox, tagged relayer and ready. Calling it again
without options does not duplicate checks. Supplying options again throws to prevent conflicting configuration.
In a standalone ServiceCollection, also register logging before resolving HealthCheckService, as required by
Microsoft's health service. Normal application hosts already provide logging.
These checks describe readiness. Do not use broker connectivity or a dispatch backlog to trigger process restarts. The core package has no health-check dependency. Health polls add no work to the message pipeline.
Explicit adapters
Native broker connectivity adapters are not supplied yet. This package supplies an outbox
health adapter for IOutboxStore; use the PostgreSQL read/claim store
or an application-supplied store.
Registering a transport does not establish native connectivity. Applications or future native
integrations register their adapters explicitly as keyed services. Without these adapters, configured transports,
consumers and stores report unhealthy. Built-in adapter work is tracked with the
Kafka,
Service Bus,
SQS/SNS,
RabbitMQ and
outbox foundation implementations:
// Adapter types implement the contracts below and use the application's existing clients/state.
builder.Services.AddKeyedSingleton<ITransportHealthProbe, OrdersConnectivity>("orders-broker");
builder.Services.AddKeyedSingleton<IConsumerHealthProvider, OrdersConsumers>("orders-broker");
// OrdersOutboxStore is an application implementation of IOutboxStore, not a built-in database provider.
builder.Services.AddKeyedScoped<IOutboxStore, OrdersOutboxStore>("orders-store");
builder.Services.AddRelayerOutboxHealthProvider("orders-store");
builder.Services.AddRelayerHealthChecks(new RelayerHealthCheckOptions
{
OutboxNames = ["orders-store"],
ConsumerStallTimeout = TimeSpan.FromMinutes(2),
OutboxDegradedThreshold = 500,
OutboxUnhealthyThreshold = 5_000
});
Use exactly the case-sensitive transport name declared in AddRelayer. Register one provider of each kind
per key. The consumer provider serves all consumer declarations on that transport. Checks resolve adapters
inside the health service's scope; adapters may be scoped if their native dependencies require it.
ITransportHealthProbe.CheckAsyncreturnsHealthCheckResultfrom a read-only native connectivity operation. Keep native status, description, data and exception. Do not publish a test message, consume a message, invent broker semantics or create a separate client per poll.IConsumerHealthProvider.GetStateAsyncreceives the exactIConsumerDefinitionfromRelayerConfiguration, so declarations on the same source remain distinct. ReturnConsumerHealthStatefrom receiver-owned state.IsIdleis true only when all workers are waiting for messages. For active workers,LastProgressAtis the oldest last-progress time, or activation time before first progress. One busy worker must not hide another stalled worker. Return null if state is unavailable; lack of progress alone does not establish idleness.IOutboxHealthProvider.GetPendingCountAsyncreturns pending rows eligible for dispatch. Exclude rows whose scheduled delivery time is in the future, parked/dead rows and rows with live claims. Return null if the count is unavailable. List every expected store inOutboxNames, including stores whose adapter is not available yet. A registeredIOutboxEnlistmentwith an emptyOutboxNamesfails health to catch an accidentally omitted declaration without constructing a scoped transaction service.
AddRelayerOutboxHealthProvider(name) registers a scoped keyed IOutboxHealthProvider that resolves the
keyed IOutboxStore with exactly the same case-sensitive name. The adapter queries
GetPendingCountAsync(now, cancellationToken) with the DI TimeProvider's current UTC instant on every poll.
It preserves null counts, errors and cancellation. EF Core integrations can use scoped store dependencies;
the health scope disposes them after the poll. Repeated registration preserves an existing provider for that name.
Applications may still register IOutboxHealthProvider directly for other storage integrations.
Register the adapter in addition to AddRelayerHealthChecks, and include its name in OutboxNames.
Adapter registration does not change the expected names, create a database store, or start dispatch. A missing
keyed store reports unhealthy. An adapter registered through this extension whose name is absent from
OutboxNames also reports unhealthy, including when another declared store is healthy.
See the durable store contract for provider requirements.
A missing provider fails its configured resource. Null consumer state or backlog, a negative count and an active
consumer's future progress timestamp also fail health. No configured resources in a category means healthy
with an explicit description. All resources in a category are checked; the worst status wins. The description
names every resource. Each result's Data retains the individual HealthCheckResult, including native data and
exceptions. Consumer data keys include transport, declaration index and source. Backlog result data includes
pending_count. The aggregate exception is the first reported exception; other exceptions remain in the data.
Idle consumers are healthy regardless of elapsed time. Active consumers become unhealthy at or beyond
ConsumerStallTimeout. Backlogs become degraded at or beyond OutboxDegradedThreshold and unhealthy at or
beyond OutboxUnhealthyThreshold. Defaults are five minutes, 1,000 rows and 10,000 rows respectively.
All thresholds must be positive; the unhealthy threshold must exceed the degraded threshold. Outbox names must
be nonblank and unique. Invalid options fail host startup through Options validation.
All adapters must observe the supplied cancellation token. Caller cancellation propagates; an unrelated native timeout or exception becomes unhealthy with the original exception and useful failure text. Health polling does not swallow cancellation or turn unavailable probes into successful results.
Configuration and Options
Use the configuration overload for immutable options; it binds explicitly without reflection and works under
Native AOT. The .NET configuration binding generator skips init-only setters, so do not use its generic Bind
for these options.
builder.Services.AddRelayerHealthChecks(builder.Configuration.GetSection("RelayerHealth"));
{
"RelayerHealth": {
"ConsumerStallTimeout": "00:02:00",
"OutboxDegradedThreshold": 500,
"OutboxUnhealthyThreshold": 5000,
"OutboxNames": ["orders-store"]
}
}
Options use IOptions<RelayerHealthCheckOptions>, IOptionsSnapshot<RelayerHealthCheckOptions> and
IOptionsMonitor<RelayerHealthCheckOptions> as usual, including registered validators. The checks read current
monitor values for each poll. Configuration reload rebinds and validates the values. Values use invariant
culture; malformed numbers or durations raise OptionsValidationException with the offending key.
Applications may replace the DI TimeProvider to control consumer-time measurements in tests.