| Name | Modified | Size | Downloads / Week |
|---|---|---|---|
| Parent folder | |||
| Quartz.NET-4.5.0.zip | 2026-10-13 | 51.9 MB | |
| README.md | 2026-10-13 | 11.1 kB | |
| v4.5.0 source code.tar.gz | 2026-10-13 | 8.9 MB | |
| v4.5.0 source code.zip | 2026-10-13 | 11.2 MB | |
| Totals: 4 Items | 72.1 MB | 0 | |
Quartz.NET 4.5 is one dashboard for a fleet. It fronts schedulers in other processes, on other machines and in other clusters, and tells apart workers that all run QuartzScheduler. Targets fronting one clustered store show as one row with its nodes. A new package, Quartz.Dashboard.Agent, lets a worker dial out to the dashboard, so the worker opens no inbound port. Six more packages are proven under native AOT on every pull request. An application on 4.4 compiles on 4.5 unchanged, and the database schema did not change.
:::shell
dotnet add package Quartz --version 4.5.0
Highlights
- A fleet, keyed
target/name. EachAddQuartzHttpClientregistration names its process withHttpClientOptions.Target, sow1/QuartzSchedulerandw2/QuartzSchedulerare two rows, beside store-attached windows and the local schedulers. Fleet - A cluster shows once. Targets that front one clustered store are detected and merged into one row,
w1+w2+w3/QuartzScheduler, whose Cluster page starts, stands by, shuts down and interrupts each node. Fleet - Workers dial out:
Quartz.Dashboard.Agent.UseDashboardAgentconnects a scheduler to a dashboard over SignalR. The dashboard enables it withAcceptAgents. The worker needs no inbound port, no HTTP API and no web framework. Dashboard agent - Secure by default. The agent refuses every job type a dashboard names until
IsJobTypeAllowedis set. The hub reads its token from theAuthorizationheader only. A hub with neither tokens nor a policy fails at startup. Dashboard agent - Native AOT, proven per package.
Quartz.Jobs,Quartz.Plugins,Quartz.Plugins.TimeZoneConverter,Quartz.HttpClient,Quartz.AspNetCoreandQuartz.Dashboard.Agentnow sayIsAotCompatible. Each one is published as a native executable and run on Windows, Linux and macOS on every pull request. Trimming and native AOT
Fleet
- Identity is
target/name(#4042, closes [#3774]). - The new public
SchedulerRef(SchedulerName, Target)is the key a dashboard addresses a scheduler by.Parsesplits at the first/. A bare name is the key of a scheduler reached through no target. HttpClientOptions.Targetnames the process. It becomes the client's service key and the first half of the key./and+are refused.- The Schedulers page shows one row per key, with Target and Reached columns. The picker groups by target, and remembers the selection in the
qz_schedulercookie. - The HTTP API's
{name}resolves to the untargeted entry only. A name held only by targets answers404, naming the targets. - A local scheduler whose own name contains
/keeps that name everywhere: in resolution, authorization, history and live logs. - A cluster behind several targets is one row (#4042).
- A fleet monitor asks every target which nodes it sees at
QuartzDashboardOptions.ClusterDetectionInterval, one minute by default. Targets on one clustered store merge intoa+b+c/name. - Membership is sticky: a node that stops answering stays in its cluster as unreachable, so the key changes on a topology change, never on an outage. Each round elects a preferred member from the targets that answered.
- The Cluster page reaches each node through its member key. A cluster-wide lifecycle verb is refused, because a cluster has no single lifecycle.
- Log events
9105–9110. SchedulerRegistrationgainsKey,MembersandLastSeenUtc.GET …/schedulersrows gainmembersandlastSeenUtc.SchedulerResource.Targetlets an authorization policy see which process it is asked about.
Dashboard agent
- New package
Quartz.Dashboard.Agent(#4044, closes [#3773]). - Worker:
UseDashboardAgent(o => { o.Endpoint = new("https://ops.example/quartz/agents"); o.Token = …; o.Target = "w1"; }). - Dashboard:
AcceptAgents(a => a.Tokens.Primary = …). - The connection retries forever, from 1 s back to 30 s. The dashboard shows the worker Unknown, with when it was last seen, after three missed heartbeats (45 s by default). It forgets the worker after
ForgetAfter, one hour by default. A restarted worker comes back on the same row. - History, live logs and every page work over the connection. History is read through the tunnel and the database schema is unchanged.
- Only the SignalR client is referenced, with no
Microsoft.AspNetCore.App. - What the agent accepts is decided on the worker.
ReadOnly,IsOperationAllowed,MaxConcurrentOperations(a bounded queue; overflow answers503),MaxPageSize, and a message cap of 4 MiB by default.IsJobTypeAllowedunset refuses every request that stores a job by type name, with403. Triggering, pausing, rescheduling and deleting existing jobs all work.- A cancelled or timed-out operation is cancelled on the worker too, whether it is queued or running.
- How the hub is authorized.
- Bearer tokens in two slots,
PrimaryandSecondary, for rotation. They are compared in constant time and read from theAuthorizationheader only, never from a query string, because a URL ends up in access logs. - Alternatively, an
AuthorizationPolicy. - A token-only hub stays reachable under a fail-closed fallback policy.
- Log events
9111–9117(dashboard) and9300–9305(agent). - One dashboard instance per deployment in agent mode: the agent registry lives in the process.
Trimming and native AOT
- Six side packages say
IsAotCompatible(#4051, part of [#3341]). Quartz.Trimming.CanaryrunsQuartz.Jobs,Quartz.PluginsandQuartz.Plugins.TimeZoneConverteras a native executable.Quartz.Trimming.Canary.WirerunsQuartz.AspNetCore,Quartz.HttpClientandQuartz.Dashboard.Agentthe same way.- The build fails on any warning a package's baseline does not record. None of the six produces an
IL3050. VerifyReferenceAotCompatibilitystops listing them.Quartz.Jobsrecords no trim warning any more.Quartz.Extensions.Redis,Quartz.Aspireand theQuartz.Weasel*packages stay trimmable without the claim, because each needs a server or a database that nothing can run natively here. Which packages say whether they can be trimmed
Scheduling and the dashboard
- A freed execution-limit slot wakes the scheduler loop (#4041, closes [#4033]). A node parked until its next look used to wait out that look when a firing ended in a group held at its limit, or when a held pinned trigger became free. Now:
- it looks again as soon as the slot frees;
- while a held trigger is re-polled early, the look-ahead is bounded so the node acquires nothing that is due after its next look.
- The Backfill dialog's preview says when Max slots would refuse the range, and how far the spacing spreads it (#4038, closes [#4037]). Thanks to @dongfo, in a first contribution.
Behaviour changes
| Before | After | What to do |
|---|---|---|
IQuartzApiClient members took a bare scheduler name |
They take a key; a bare name is the key of a scheduler reached through no target | Nothing for a 4.4 deployment. A replacement client parses keys with SchedulerRef.Parse |
The HTTP API's {name} resolved to the first entry under the name |
It resolves to the untargeted entry only | Nothing for a 4.4 deployment |
AddQuartzHttpClient was keyed by the scheduler name |
It is keyed by Target when one is set |
If you set Target, resolve GetRequiredKeyedService<IScheduler>(target) |
A second Target of the same name was impossible |
It throws SchedulerConfigException |
Give each process a Target of its own |
AddQuartzDashboard() registered no scheduler repository |
It registers Quartz's shared services, so an agent-only dashboard needs no AddQuartz |
Nothing |
| The Schedulers page showed one row per name | It shows one row per key, and one row per detected cluster | Nothing |
Upgrading
An application on 4.4 compiles on 4.5 unchanged. No migration to run: the schema is the same, and 4.4 and 4.5 nodes run side by side on one store. Every addition and behaviour change is in the migration guide's Upgrading from 4.4 to 4.5.
Public API
Additions only: +66 lines, none removed, and no interface gained a member.
| Assembly | Lines added |
|---|---|
| Quartz.Dashboard | 24 |
| Quartz.Dashboard.Agent (new) | 23 |
| Quartz | 15 |
| Quartz.HttpClient | 3 |
| Quartz.AspNetCore | 1 |
Quartz.Dashboard now depends on Quartz.HttpClient, at the same version. The whole delta, member by member: git diff v4.4.0 v4.5.0 -- src/Quartz.Tests.Unit/Verify src/Quartz.Tests.AspNetCore/Verify.
Also
- The docs build takes patched npm dependencies (#4040). Test, analyzer and Aspire hosting packages were updated.
- The comparison page describes 4.5 (#4050).
- Marten and Wolverine are held at 9.45 and 6.45 in the tests and examples while the Weasel floor holds at 9.39.0 (#4056).
What's Changed
Other changes
- Main builds 4.5.0, and the benchmark notes record what 4.4.0 measured by @lahma in https://github.com/quartznet/quartznet/pull/4035
- The docs build takes the patched shell-quote, proxy-addr, compression, source-map-js, Vue, smol-toml, postcss-selector-parser and KaTeX by @lahma in https://github.com/quartznet/quartznet/pull/4040
- A freed execution-limit slot wakes the scheduler loop, and a node looking again early for a held trigger acquires nothing due after its next look by @lahma in https://github.com/quartznet/quartznet/pull/4041
- Call UpdatePreview on spacing and max slots change by @dongfo in https://github.com/quartznet/quartznet/pull/4038
- The dashboard keys a scheduler by target/name and shows targets fronting one cluster as one row by @lahma in https://github.com/quartznet/quartznet/pull/4042
- A scheduler dials out to a dashboard with Quartz.Dashboard.Agent and is shown and driven over that connection by @lahma in https://github.com/quartznet/quartznet/pull/4044
- The comparison carries what 4.5 added by @lahma in https://github.com/quartznet/quartznet/pull/4050
- Six side packages run as native executables on every pull request and say IsAotCompatible by @lahma in https://github.com/quartznet/quartznet/pull/4051
- Dependabot holds Marten and Wolverine minor and patch updates while the Weasel floor holds by @lahma in https://github.com/quartznet/quartznet/pull/4056
- The agent page and readme agree on the key and the trimming claim, and the CI legs get the time they now take by @lahma in https://github.com/quartznet/quartznet/pull/4057
New Contributors
- @dongfo made their first contribution in https://github.com/quartznet/quartznet/pull/4038
Full Changelog: https://github.com/quartznet/quartznet/compare/v4.4.0...v4.5.0