Table of Contents

Class QuartzHealthCheckOptions

Namespace
Quartz
Assembly
Quartz.dll
public sealed class QuartzHealthCheckOptions
Inheritance
QuartzHealthCheckOptions
Inherited Members

Examples

options.RequireSuccessWithin(new JobKey("nightly-report", "reports"), TimeSpan.FromHours(26));

Properties

ClusterCheckinTolerance

How many of its own check-in intervals a clustered node may go without checking in before the check reports Degraded. 3 by default; null or 0 turns the reading off.

public double? ClusterCheckinTolerance { get; set; }

Property Value

double?

Remarks

The rest of this check asserts that the scheduler can fire and that its store answers. Neither says anything about the cluster manager, which runs on its own timer: a node whose check-in loop has wedged still answers a store query, still reports Running, and is meanwhile being recovered by its peers — they take its triggers because it looks dead to them, while its own probe says it is fine.

So a clustered scheduler is also asked when it last checked in, through SchedulerQueryExtensions' cluster listing, and a last check-in older than this many of the node's own configured intervals is reported as degraded rather than healthy. Three because that is the same order of magnitude cluster recovery uses to decide a node is gone — one interval is an ordinary scheduling delay, and a node that has missed three is not merely late.

The reading happens only when the store says it is clustered, so a scheduler on an in-memory or unclustered store makes no extra query. Set this to null or 0 to make no query at all.

FailureStatus

The HealthStatus reported when the check fails. When null the default (Unhealthy) is used.

public HealthStatus? FailureStatus { get; set; }

Property Value

HealthStatus?

Name

The name used to register the health check.

public string? Name { get; set; }

Property Value

string

Remarks

Left unset, the check is called quartz-scheduler, or quartz-scheduler-<scheduler name> for a named scheduler — which is why it is nullable rather than carrying one of those as its value: the name depends on which scheduler the check is for, and that is not known to an options object.

RequiredJobs

Jobs that must have succeeded recently. Empty by default, which reads nothing.

public List<RequiredJobOptions> RequiredJobs { get; }

Property Value

List<RequiredJobOptions>

Remarks

A requirement is met while the job's LastSucceededAtUtc is within its SucceededWithin of now, on the scheduler's clock; a skipped run counts as a success. A job with no recorded success is judged from the first time the check evaluated it, so one that has not run within its window since then fails. A job that is not met reports its Status, and the check reports the worst of that and the scheduler's own verdict. The report's data carries one entry per job that is not met.

The statuses are read from the scheduler's execution history, in one QueryJobRunStatuses(JobRunStatusQuery, CancellationToken) per check. A scheduler with no history, or a history that keeps no status, fails validation at startup.

One requirement per job: when a job is listed twice, the later entry wins. Get-only, like every collection on a Quartz options type, so a configuration section binds into it; add to it, or call RequireSuccessWithin(JobKey, TimeSpan, HealthStatus).

StaleFiringTolerance

How many of the store's own misfire thresholds a trigger may be overdue before the check reports Degraded, and half of how many before it reports Unhealthy. null by default, which turns the reading off; 0 does the same. 3 is the value to start from.

public double? StaleFiringTolerance { get; set; }

Property Value

double?

Remarks

This is the silent stall: nothing has fired for a while although something should have. A scheduler in that state passes everything else this check asks — it is Running, its store answers, its cluster manager may well still be checking in — because none of those questions is about work actually leaving the queue. A wedged scheduler thread, a thread pool with nothing free, a lock nobody releases and a job store whose acquisition query has begun timing out all look identical from outside and all look healthy.

So the check asks the store for a trigger that is schedulable (Normal) and whose next fire time has passed by more than this many misfire thresholds. Finding one means the scheduler is late by more than lateness is defined to be; finding one twice as late is reported as unhealthy, because a backlog that keeps growing is no longer a delay. The overdue trigger's key and the instant it was due are in the report's data, so an operator sees which trigger is waiting and since when.

The misfire threshold is the unit because it is the store's own definition of "late enough to matter" — MisfireThreshold or MisfireThreshold, whichever this scheduler runs, and one minute for a store that has neither. It is also, on the database store, the default sweep interval of the misfire handler, which is why the multiplier has to be more than one: a trigger can be a threshold late before it counts as misfired and another sweep late before the handler reaches it, so anything under 3 can report ordinary recovery as a stall.

Off by default, unlike ClusterCheckinTolerance, because what counts as overdue is the application's to say: a scheduler whose triggers are deliberately paused-by-calendar, or one that is meant to run a backlog down after a maintenance window, is not stalled. A standby or paused scheduler is never read this way at all — those are deliberate, and the check reports them as it always has.

StandbyStatus

The HealthStatus reported while the scheduler is in Standby. When null the default (Degraded) is used.

public HealthStatus? StandbyStatus { get; set; }

Property Value

HealthStatus?

Remarks

Degraded is the right default and the wrong answer for some deployments. Standby is deliberate and reversible, so calling it healthy would hide an application that never started its scheduler and calling it unhealthy would take a node out of rotation for doing what it was told. But a report is only read by whoever it reaches: an ASP.NET Core application can remap degraded on the endpoint with MapHealthChecks(…, new HealthCheckOptions { ResultStatusCodes = … }), while a worker has no endpoint at all — its only reader is the probe that asks the HealthCheckService directly, and a standby node it must not route to has to say Unhealthy here.

This is the standby verdict alone. A scheduler still in Created because AutoStart is false also reports degraded, and keeps doing so: that is a window between the host starting and the application pressing start, not a state a node sits in.

Tags

Tags associated with the health check, allowing it to be filtered (for example into separate liveness and readiness probes).

public List<string> Tags { get; }

Property Value

List<string>

Remarks

Get-only with an in-place initializer, like every other collection on a Quartz options type: a configuration binder binds into a non-null collection without needing a setter, and one configure callback cannot discard the tags another added. Add to it — options.Tags.Add("ready") — rather than assigning a new collection.

UnservedExecutionGroupTolerance

How many of the store's own misfire thresholds a schedulable trigger in an execution group this node does not serve may be overdue before the check reports Degraded. 3 by default; null or 0 turns the reading off.

public double? UnservedExecutionGroupTolerance { get; set; }

Property Value

double?

Remarks

The trap in routing a group to some nodes: when none of them is live, nothing acquires the group's triggers and — because a node's misfire sweep handles only the groups it serves — nothing re-arms them either. They wait, unmodified, and from a node that does not serve them that is invisible unless something looks. So a node whose ServedExecutionGroups or RefusedExecutionGroups is set asks the store for the schedulable triggers overdue by more than this many misfire thresholds, and reports the groups among them it does not serve, with a count each and the oldest fire time.

Three, for the reason StaleFiringTolerance suggests it: a serving node's sweep re-arms a misfired trigger within about a threshold of it crossing the line, so one overdue by three has had no serving node's sweep reach it. Degraded rather than unhealthy, because the reporting node is fine and taking it out of rotation helps nobody. A node that routes nothing makes no query.

Methods

RequireSuccessWithin(JobKey, TimeSpan, HealthStatus)

Requires job to have succeeded within within, replacing any requirement already given for it.

public void RequireSuccessWithin(JobKey job, TimeSpan within, HealthStatus status = HealthStatus.Degraded)

Parameters

job JobKey

The job.

within TimeSpan

How long ago its last success may have fired. Must be positive.

status HealthStatus

What the check reports while the requirement is not met: Degraded or Unhealthy.