HealthChecks state #66117
IdeaCurrently, there is no way to retrieve the current health checks state directly. To get the status/state, it is necessary to resolve the If either The proposed approach is to register as singleton a transfer object (ie That would make a way to easly get the health checks state as well as reduce number of calls to registered health check endpoints (as those are made by either Motivation
I think, I can contribute on that, as this seems to be quite easy. |
Replies: 1 comment 1 reply
|
There is currently no shared “current health state” in the built-in implementation. The endpoint and the publisher are intentionally separate executions. The health-check middleware calls The publisher hosted service independently calls The That separation is useful because a health report is not a single timeless application property. Different consumers may use different predicates/tags, periods and timeouts, and a cached report can become stale. For your first scenario, your proposed state holder is still a good application-level pattern. I would implement it as the publisher itself so that no additional health-check run is needed: public sealed class HealthState : IHealthCheckPublisher
{
public sealed record Snapshot(
HealthReport Report,
DateTimeOffset RefreshedAt);
private Snapshot? _current;
public Snapshot? Current => Volatile.Read(ref _current);
public Task PublishAsync(
HealthReport report,
CancellationToken cancellationToken)
{
Volatile.Write(
ref _current,
new Snapshot(report, DateTimeOffset.UtcNow));
return Task.CompletedTask;
}
}Register the same singleton under both service types: builder.Services.AddSingleton<HealthState>();
builder.Services.AddSingleton<IHealthCheckPublisher>(sp =>
sp.GetRequiredService<HealthState>());
builder.Services.Configure<HealthCheckPublisherOptions>(options =>
{
options.Predicate = registration =>
registration.Tags.Contains("ready");
options.Delay = TimeSpan.Zero;
options.Period = TimeSpan.FromSeconds(10);
});The worker should treat all three cases conservatively: no snapshot yet, an old snapshot, and a non-healthy report. For example, require I would not make There is a separate issue with the deployment use case: Kubernetes readiness is a per-pod traffic-routing signal. A new pod becoming ready does not prove that the old pod has terminated, and an in-process health snapshot cannot coordinate two replicas. If exactly one replica may process a job, use queue consumer semantics, a database/distributed lease, or Kubernetes leader election. Health checks can prevent a worker from starting work when its own dependencies are unhealthy, but they should not be the exclusivity mechanism. So I would support a reusable publisher-backed state holder as an application pattern, but not a universal framework cache: its predicate, freshness policy and failure behavior must be explicit for each consumer. If this addresses both scenarios, you can mark it as the accepted answer so future readers can find the distinction between health state and coordination. |
There is currently no shared “current health state” in the built-in implementation. The endpoint and the publisher are intentionally separate executions.
The health-check middleware calls
HealthCheckService.CheckHealthAsyncfor every request:https://github.com/dotnet/aspnetcore/blob/main/src/Middleware/HealthChecks/src/HealthCheckMiddleware.cs#L47-L72
The publisher hosted service independently calls
CheckHealthAsyncon its timer and then passes that report to every registeredIHealthCheckPublisher:https://github.com/dotnet/aspnetcore/blob/main/src/HealthChecks/HealthChecks/src/HealthCheckPublisherHostedService.cs#L146-L178
The
IHealthCheckPublisherdocumentation also explicitly says tha…