Skip to content

Bump the nuget-minor-patch group with 6 updates - #221

Closed
dependabot[bot] wants to merge 2 commits into
mainfrom
dependabot/nuget/nuget-minor-patch-565cbd0284
Closed

dependabot[bot] wants to merge 2 commits into
mainfrom
dependabot/nuget/nuget-minor-patch-565cbd0284

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

⚠️ Dependabot is rebasing this PR ⚠️

Rebasing might not happen immediately, so don't worry if this takes some time.

Note: if you make any changes to this PR yourself, they will take precedence over the rebase.


Updated Marten from 9.39.0 to 9.39.1.

Release notes

Sourced from Marten's releases.

9.39.1

What's Changed

Full Changelog: JasperFx/marten@V9.39.0...v9.39.1

Commits viewable in compare view.

Updated Quartz.Extensions.Hosting from 4.1.1 to 4.2.0.

Release notes

Sourced from Quartz.Extensions.Hosting's releases.

4.2.0

Quartz.NET 4.2 is an additive minor: every public change is a new type, a new member on a type we own, or a default interface member, and its one schema change is a generated migration under database/migrations/4.2/ that a mixed 4.1/4.2 cluster runs safely. A 4.0 or 4.1 application upgrades by changing the version and, on a persistent store, running that migration before the first 4.2 node starts. The headline is that a job can be declared on its class — [QuartzJob] and [CronTrigger] — with the cron expression checked by the compiler, and that one trigger can wait, in the store, for another's outcome.

dotnet add package Quartz --version 4.2.0

Highlights

  • A cron or timeout literal that cannot parse is a build error. Quartz.nupkg now carries an analyzer under analyzers/dotnet/cs: QZ0001 refuses a cron literal the parser would refuse — at WithCronSchedule, CronScheduleBuilder.Create, the CronExpression constructors and parse members, CronCalendar and CronTriggerImpl, honouring a literal CronFormat.Unix — with the parser's own message; QZ0002 refuses a [JobTimeout("…")] that does not parse or is negative; QZ0003 warns on [PersistJobDataAfterExecution] without [DisallowConcurrentExecution]; QZ0004 (Info) notes an Execute that never observes its cancellation token. The analyzer is netstandard2.0 and links the cron parser's own sources rather than a second grammar, so a literal the compiler accepts is one the scheduler accepts; a parity corpus of 128 expressions pins that. <DisableQuartzAnalyzers>true</DisableQuartzAnalyzers> in the project file turns it off (#​3846) — ExcludeAssets="analyzers" does not, on the .NET 10 SDK; the package's dependencies are unchanged. (#​3803, #​3815)
  • A job declares itself on its class, and the compiler writes the registration. [QuartzJob(Name, Group, Description, Durable, RequestRecovery, Scheduler)] on an IJob and one [CronTrigger("…", Name, Group, TimeZone, MisfireInstruction, Priority, Description, ExecutionGroup)] per schedule are read by a source generator inside Quartz.nupkg, which writes an AddDeclaredJobs() extension for IQuartzBuilder calling the same AddJob<T>/AddTrigger<T> you would have written — no reflection, nothing to root for trimming, and the cron expression checked at compile time by QZ0001. QZ1001–QZ1003 refuse an attribute on a type that is not a concrete IJob, two declarations with one identity, and a [CronTrigger] without [QuartzJob]. Scheduler = "…" binds a declaration to one named scheduler. [SimpleTrigger] and a cron read from configuration are the deliberate follow-ups. (#​3804, #​3818)
  • A trigger can wait, in the store, for another trigger's outcome. TriggerBuilder.StartAfter(parentKey, condition) — or scheduler.ScheduleJob<TJob,TInput>(input, Continuation.After(parentKey), …) for a one-off — stores a trigger in the new state Awaiting; when the parent's firing ends, its completion settles every continuation inside its own transaction, on whichever node ran it: a matching outcome (OnSuccess, OnFailure, OnCancellation, OnVeto, or OnAnyOutcome) releases the trigger to fire now, any other discards it, and a scheduled retry settles nothing. A parent deleted while continuations wait releases the OnAnyOutcome ones and parks the rest in Error. Every store call that completes a firing now carries ExecutionOutcome and the exception through TriggeredJobCompleteContext. JobChainingJobListener keeps the recurring case and gains a condition. A continuation can be declared in quartz_jobs.xml (<continues-after>, <continuation-condition>), in quartz_jobs.json and the Quartz:Schedule section (ContinuesAfter, ContinuationCondition), scheduled over the wire, and seen in the dashboard — an Awaiting filter on Triggers, and "Continues after" and "When" on a trigger's detail. Job Continuations is the page. (#​3805, #​3806, #​3819, #​3821)
  • A retry policy that gives up says so. IJobExecutionContext.Outcome and RetryScheduled tell a listener what the job did and whether a retry follows; ITriggerListener.TriggerRetriesExhausted is raised once when a policy runs out (a default interface member, so no listener needs to change); the counter quartz.trigger.retries_exhausted counts it; execution history records the attempt and whether a retry was scheduled, the History page and the history routes filter on "failed after retries", and a final failure on the History page has a Run again button, recorded in the Action Log. RetryPolicy.Exponential takes an optional jitter that spreads the attempts. One correction rides along: IJobExecutionContext.RetryAttempt now reports the attempt the firing is to listeners as well — it used to read the trigger's field after ExecutionComplete had already advanced or cleared it. (#​3807, #​3829)
  • A cron expression's next occurrence is eleven times faster and asks the time zone nothing on the steady-state path. GetNextValidTimeAfter used to make eight TimeZoneInfo queries per answer; it now walks the expression's own bitmasks on the wall clock and resolves the offset through a per-zone table of safe segments — intervals verified against the zone's own answers, with everything within 48 hours of a transition, and every zone the table cannot verify, left to the existing code. Every DST edge case still runs the code that handled it before, and a differential test compares the two paths to the tick across ten zones, half of its instants near transitions. On the machine the benchmark README names: 0 0/5 * * * ? 421 → 36.5 ns and 100 successive occurrences 43.1 → 3.5 µs, zero allocations, against NCrontab's 25.3 ns and 2.7 µs; the second-level form now beats NCrontab. The one cost is 8 bytes on each CronExpression. (#​3801, #​3820)
  • Quartz is measured against the schedulers it is compared to, and the numbers are published. src/Quartz.Benchmark.Competitors/ runs the same five workloads against TickerQ 10.4.0 and Hangfire 1.8.25 — firing throughput in memory and on PostgreSQL, schedule-to-execute latency, punctuality of one-second schedules, and the cost of one schedule call — with completion counted inside the executing job on every side, and the benchmark README and the operations page carry the tables, the rows Quartz loses included. On the box the README names, Quartz at its defaults executes 181,000–257,000 in-memory one-offs a second (TickerQ 98,000–115,000, Hangfire 58,000–92,000), reaches Execute 58–70 µs after ScheduleJob (TickerQ 14.7 ms, Hangfire 94–235 µs), and fires 100 % of one-second schedules within 50 ms (TickerQ 73–78 %, Hangfire 0–15 %); a schedule call costs 7.7–10 µs against TickerQ's 1.1–2.4 µs, and on PostgreSQL a one-off firing makes too many round trips — which #​3824 addresses. The profiling switches that found where the time goes (--profile-fire, --profile-cron, --profile-schedule, --latency) ship in Quartz.Benchmark, and out-of-process benchmark runs work again. On RAMJobStore a firing now allocates 1.83 KB where 4.1 allocated 2.63 KB and dispatches with two fewer thread hand-offs — an empty job data map costs nothing to clone or build, one task per dispatch instead of three, one ambient slot per firing, the run shell handed to the pool as state (IThreadPool.TryRunWithState, a default interface member), the firing's clock read only when a span is listening — 12–14 % faster on the fire-throughput benchmark. And one durable job with thousands of triggers behind it — the one-off API's own shape — no longer costs the store a linear scan and an array of keys on every completion: churn against a job with 20,000 triggers went from 183 µs and 157 KB to 434 ns and 424 B. (#​3802, #​3816, #​3822, #​3823, #​3826)
  • Execution history can live in the database, and the store keeps it trimmed. UsePersistentStore(s => s.UseExecutionHistory()) — or quartz.jobStore.executionHistory = true — records every execution and misfire into QRTZ_EXECUTION_HISTORY and QRTZ_MISFIRE_HISTORY in the scheduler's own database, so a cluster has one history and every node's dashboard, the HTTP API's history routes and a store-attached window read the same rows. Retention is the store's: a sweep on the scheduler's clock deletes past ExecutionHistoryOptions.Retention and trims to MaxEntriesPerScheduler, idempotently, so several nodes sweeping at once is harmless. Writes go on their own connection, never inside a job's transaction, and a failed write is logged and dropped rather than failing the firing. The tables are optional: a 4.2 node validates them only when the history is on, and names the script when they are missing. (#​3771, #​3825)
  • The dashboard can be pointed at a database and show every scheduler in it. AddQuartzDashboard(o => o.AttachStore("prod", store => store.UseSqlServer(cs))) discovers the schedulers a database holds and opens a window on each — a never-started scheduler over the shared store, listed with the new origin Window and the target's name, its status derived from the cluster's check-in rows rather than from itself (no rows reads as unknown, not as stopped), with every scheduling action available and the node-local ones — start, stand-by, shutdown, interrupt — hidden with a sentence saying they need an API or agent target. With the database-backed history store the window's History page shows what the nodes ran. This is the shared-storage reach model a cluster behind a load balancer needs, with zero worker changes and no inbound port; the docs say what a window can and cannot do and that it needs the cluster's serializer configuration. (#​3772, #​3828)
  • The documentation compares, and says where Quartz loses. A comparison page puts Quartz.NET beside Hangfire 1.8.25, TickerQ 10.4.0, Wolverine 6.35 and Coravel 6.0, one sourced table per concern, with a section collecting the places Quartz is the more expensive answer; two guides map Hangfire's and TickerQ's APIs to ours and name the differences that bite (six cron fields and a different default time zone, [Queue] → execution groups that bound but do not route, retries held in the store rather than the worker, a dashboard that refuses to start until told who may reach it). The best-practices page no longer says Quartz has no retry policy, the quick start leads with the three-line form, and the index says what nobody knew was in the box. (#​3808, #​3813, #​3814)
  • The health check can say a scheduler has stopped firing. QuartzHealthCheckOptions.StaleFiringTolerance (off unless set; 3 is the documented starting value) asks the store for a schedulable trigger whose fire time passed more than that many misfire thresholds ago and reports Degraded, or Unhealthy past twice the bar — the silent stall that "running" and "store answers" never caught, with the overdue trigger, when it was due and by how much in the report's data. The worse of the check-in and stalled-firing verdicts now wins, where a late check-in used to hide a stall. Underneath, TriggerQuery.NextFireTimeBefore is a new filter every store and the HTTP API honour. (#​3809, #​3817)

Public API — additive only

Added What it is
[QuartzJob], [CronTrigger] the attributes the source generator reads; AddDeclaredJobs() is generated into your assembly
Continuation, ContinuationCondition, ExecutionOutcome, TriggeredJobCompleteContext, AwaitingContinuation a continuation, the outcomes that release it, what a firing did, and the context a store's completion receives
ITrigger.Continuation, ITriggerConfigurator<TJob>.StartAfter — default interface members; TriggerBuilder<TJob>.StartAfter; ScheduleJob<TJob, TInput>(input, Continuation after, options); JobChainingJobListener.AddJobChainLink(first, second, condition) and JobExecutionVetoed declaring a continuation, in the builder, in the one-off call, or on the recurring listener
IJobStore.FiringComplete(TriggeredJobCompleteContext) — DIM; IDriverDelegate.SelectAwaitingContinuations, ReleaseContinuation, ResetContinuationFireTime, SelectSchedulerNames — DIMs what a store and a dialect implement to settle continuations and to be discovered by a window; a store from outside this repository keeps working unchanged
TriggerState.Awaiting, StoredTriggerState.Awaiting; TriggerHeader.ContinuesAfter, ContinuationCondition; TriggerQuery.NextFireTimeBefore; the AdoConstants names of the new columns, tables and state the state on the wire and in the store, and the two new query filters
IJobExecutionContext.Outcome, RetryScheduled; ITriggerListener.TriggerRetriesExhausted — DIMs; ExecutionHistoryEntry.RetryAttempt, RetryScheduled; ExecutionHistoryQuery.FailedFinally; RetryPolicy.Exponential(…, jitter) and RetryPolicy.Jitter; the meter quartz.trigger.retries_exhausted the retry signal, end to end
IPersistentStoreBuilder.UseExecutionHistory() — DIM; AdoJobStoreOptions.ExecutionHistory history in the database
SchedulerOrigin.Window; SchedulerRegistration.Target; QuartzDashboardOptions.AttachStore, AttachStoreOptions; SchedulerHeaderDto.Target, IsWindow, DisplayName store-attached targets
QuartzHealthCheckOptions.StaleFiringTolerance the stalled-firing reading
IThreadPool.TryRunWithState — DIM the pool dispatch that carries state instead of a closure
ObjectDoesNotExistException what scheduling a continuation of a trigger the store does not hold throws, naming both keys; it round-trips over the HTTP API

Nothing was removed or reshaped. Baseline diff v4.1.1..v4.2.0: Quartz 111 added member lines, 0 removed, twelve of them default interface members; Quartz.Dashboard 12 added, 0 removed (24 lines re-ordered by the generator); Quartz.AspNetCore and Quartz.HttpClient unchanged. The Quartz package now depends on nothing new and carries analyzers/dotnet/cs/Quartz.Analyzers.dll and buildTransitive/net10.0/Quartz.targets, which reads DisableQuartzAnalyzers.

Schema

database/migrations/4.2/add_continuations_<dialect>.sql adds CONTINUES_TRIGGER_NAME, CONTINUES_TRIGGER_GROUP and CONTINUATION_CONDITION to QRTZ_TRIGGERS, nullable. Required from 4.2.0: a 4.2 node refuses to start against a database without them, naming the script. Safe to apply while 4.0 and 4.1 nodes run — they never read the columns — but a 4.1 node cannot settle a continuation, so migrate, roll every node, then start scheduling them. 4.2/add_execution_history_<dialect>.sql adds QRTZ_EXECUTION_HISTORY and QRTZ_MISFIRE_HISTORY, optional: needed only with UseExecutionHistory(); a fresh install and ProvisionSchema() create them regardless.

Behaviour changes worth noting

  • TriggerState.Awaiting is a new value on the wire; a 4.1 Quartz.HttpClient reading a 4.2 host that has continuations sees a state it does not know — upgrade the client. A store outside this repository that overrides TriggeredJobComplete keeps working (FiringComplete forwards to it) and settles no continuations until it implements the new member.
  • Quartz.nupkg carries an analyzer. A project whose cron or timeout literals were wrong builds no longer — that is the point — and a build with TreatWarningsAsErrors sees QZ0003 as an error; <DisableQuartzAnalyzers>true</DisableQuartzAnalyzers> opts out, .editorconfig re-tunes a severity.
  • A continuation needs its parent to exist when it is scheduled. Storing one whose parent trigger the store does not hold throws ObjectDoesNotExistException instead of waiting for ever. A scheduling file may still name a parent declared later in the same file — triggers are stored parent-first — but a parent in another file, or scheduled by a later call, has to come first.
  • A released continuation is an ordinary trigger. It forgets its parent (its ContinuesAfter reads null), waits behind its job when that job disallows concurrent execution and is running, honours its calendar and end time — one released past its end time is discarded — and a continuation that is discarded takes everything waiting on it with it.
  • A store-attached window cannot be started. Start() on one — from code or over the HTTP API, which also refuses standby and shutdown for it — throws before anything runs, where it used to run the cluster's start-up recovery against a live database. During a mixed 4.1/4.2 window, do not pause, resume or reschedule a continuation from a 4.1 node.
    ... (truncated)

Commits viewable in compare view.

Updated Quartz.Serialization.SystemTextJson from 4.1.1 to 4.2.0.

Release notes

Sourced from Quartz.Serialization.SystemTextJson's releases.

4.2.0

Quartz.NET 4.2 is an additive minor: every public change is a new type, a new member on a type we own, or a default interface member, and its one schema change is a generated migration under database/migrations/4.2/ that a mixed 4.1/4.2 cluster runs safely. A 4.0 or 4.1 application upgrades by changing the version and, on a persistent store, running that migration before the first 4.2 node starts. The headline is that a job can be declared on its class — [QuartzJob] and [CronTrigger] — with the cron expression checked by the compiler, and that one trigger can wait, in the store, for another's outcome.

dotnet add package Quartz --version 4.2.0

Highlights

  • A cron or timeout literal that cannot parse is a build error. Quartz.nupkg now carries an analyzer under analyzers/dotnet/cs: QZ0001 refuses a cron literal the parser would refuse — at WithCronSchedule, CronScheduleBuilder.Create, the CronExpression constructors and parse members, CronCalendar and CronTriggerImpl, honouring a literal CronFormat.Unix — with the parser's own message; QZ0002 refuses a [JobTimeout("…")] that does not parse or is negative; QZ0003 warns on [PersistJobDataAfterExecution] without [DisallowConcurrentExecution]; QZ0004 (Info) notes an Execute that never observes its cancellation token. The analyzer is netstandard2.0 and links the cron parser's own sources rather than a second grammar, so a literal the compiler accepts is one the scheduler accepts; a parity corpus of 128 expressions pins that. <DisableQuartzAnalyzers>true</DisableQuartzAnalyzers> in the project file turns it off (#​3846) — ExcludeAssets="analyzers" does not, on the .NET 10 SDK; the package's dependencies are unchanged. (#​3803, #​3815)
  • A job declares itself on its class, and the compiler writes the registration. [QuartzJob(Name, Group, Description, Durable, RequestRecovery, Scheduler)] on an IJob and one [CronTrigger("…", Name, Group, TimeZone, MisfireInstruction, Priority, Description, ExecutionGroup)] per schedule are read by a source generator inside Quartz.nupkg, which writes an AddDeclaredJobs() extension for IQuartzBuilder calling the same AddJob<T>/AddTrigger<T> you would have written — no reflection, nothing to root for trimming, and the cron expression checked at compile time by QZ0001. QZ1001–QZ1003 refuse an attribute on a type that is not a concrete IJob, two declarations with one identity, and a [CronTrigger] without [QuartzJob]. Scheduler = "…" binds a declaration to one named scheduler. [SimpleTrigger] and a cron read from configuration are the deliberate follow-ups. (#​3804, #​3818)
  • A trigger can wait, in the store, for another trigger's outcome. TriggerBuilder.StartAfter(parentKey, condition) — or scheduler.ScheduleJob<TJob,TInput>(input, Continuation.After(parentKey), …) for a one-off — stores a trigger in the new state Awaiting; when the parent's firing ends, its completion settles every continuation inside its own transaction, on whichever node ran it: a matching outcome (OnSuccess, OnFailure, OnCancellation, OnVeto, or OnAnyOutcome) releases the trigger to fire now, any other discards it, and a scheduled retry settles nothing. A parent deleted while continuations wait releases the OnAnyOutcome ones and parks the rest in Error. Every store call that completes a firing now carries ExecutionOutcome and the exception through TriggeredJobCompleteContext. JobChainingJobListener keeps the recurring case and gains a condition. A continuation can be declared in quartz_jobs.xml (<continues-after>, <continuation-condition>), in quartz_jobs.json and the Quartz:Schedule section (ContinuesAfter, ContinuationCondition), scheduled over the wire, and seen in the dashboard — an Awaiting filter on Triggers, and "Continues after" and "When" on a trigger's detail. Job Continuations is the page. (#​3805, #​3806, #​3819, #​3821)
  • A retry policy that gives up says so. IJobExecutionContext.Outcome and RetryScheduled tell a listener what the job did and whether a retry follows; ITriggerListener.TriggerRetriesExhausted is raised once when a policy runs out (a default interface member, so no listener needs to change); the counter quartz.trigger.retries_exhausted counts it; execution history records the attempt and whether a retry was scheduled, the History page and the history routes filter on "failed after retries", and a final failure on the History page has a Run again button, recorded in the Action Log. RetryPolicy.Exponential takes an optional jitter that spreads the attempts. One correction rides along: IJobExecutionContext.RetryAttempt now reports the attempt the firing is to listeners as well — it used to read the trigger's field after ExecutionComplete had already advanced or cleared it. (#​3807, #​3829)
  • A cron expression's next occurrence is eleven times faster and asks the time zone nothing on the steady-state path. GetNextValidTimeAfter used to make eight TimeZoneInfo queries per answer; it now walks the expression's own bitmasks on the wall clock and resolves the offset through a per-zone table of safe segments — intervals verified against the zone's own answers, with everything within 48 hours of a transition, and every zone the table cannot verify, left to the existing code. Every DST edge case still runs the code that handled it before, and a differential test compares the two paths to the tick across ten zones, half of its instants near transitions. On the machine the benchmark README names: 0 0/5 * * * ? 421 → 36.5 ns and 100 successive occurrences 43.1 → 3.5 µs, zero allocations, against NCrontab's 25.3 ns and 2.7 µs; the second-level form now beats NCrontab. The one cost is 8 bytes on each CronExpression. (#​3801, #​3820)
  • Quartz is measured against the schedulers it is compared to, and the numbers are published. src/Quartz.Benchmark.Competitors/ runs the same five workloads against TickerQ 10.4.0 and Hangfire 1.8.25 — firing throughput in memory and on PostgreSQL, schedule-to-execute latency, punctuality of one-second schedules, and the cost of one schedule call — with completion counted inside the executing job on every side, and the benchmark README and the operations page carry the tables, the rows Quartz loses included. On the box the README names, Quartz at its defaults executes 181,000–257,000 in-memory one-offs a second (TickerQ 98,000–115,000, Hangfire 58,000–92,000), reaches Execute 58–70 µs after ScheduleJob (TickerQ 14.7 ms, Hangfire 94–235 µs), and fires 100 % of one-second schedules within 50 ms (TickerQ 73–78 %, Hangfire 0–15 %); a schedule call costs 7.7–10 µs against TickerQ's 1.1–2.4 µs, and on PostgreSQL a one-off firing makes too many round trips — which #​3824 addresses. The profiling switches that found where the time goes (--profile-fire, --profile-cron, --profile-schedule, --latency) ship in Quartz.Benchmark, and out-of-process benchmark runs work again. On RAMJobStore a firing now allocates 1.83 KB where 4.1 allocated 2.63 KB and dispatches with two fewer thread hand-offs — an empty job data map costs nothing to clone or build, one task per dispatch instead of three, one ambient slot per firing, the run shell handed to the pool as state (IThreadPool.TryRunWithState, a default interface member), the firing's clock read only when a span is listening — 12–14 % faster on the fire-throughput benchmark. And one durable job with thousands of triggers behind it — the one-off API's own shape — no longer costs the store a linear scan and an array of keys on every completion: churn against a job with 20,000 triggers went from 183 µs and 157 KB to 434 ns and 424 B. (#​3802, #​3816, #​3822, #​3823, #​3826)
  • Execution history can live in the database, and the store keeps it trimmed. UsePersistentStore(s => s.UseExecutionHistory()) — or quartz.jobStore.executionHistory = true — records every execution and misfire into QRTZ_EXECUTION_HISTORY and QRTZ_MISFIRE_HISTORY in the scheduler's own database, so a cluster has one history and every node's dashboard, the HTTP API's history routes and a store-attached window read the same rows. Retention is the store's: a sweep on the scheduler's clock deletes past ExecutionHistoryOptions.Retention and trims to MaxEntriesPerScheduler, idempotently, so several nodes sweeping at once is harmless. Writes go on their own connection, never inside a job's transaction, and a failed write is logged and dropped rather than failing the firing. The tables are optional: a 4.2 node validates them only when the history is on, and names the script when they are missing. (#​3771, #​3825)
  • The dashboard can be pointed at a database and show every scheduler in it. AddQuartzDashboard(o => o.AttachStore("prod", store => store.UseSqlServer(cs))) discovers the schedulers a database holds and opens a window on each — a never-started scheduler over the shared store, listed with the new origin Window and the target's name, its status derived from the cluster's check-in rows rather than from itself (no rows reads as unknown, not as stopped), with every scheduling action available and the node-local ones — start, stand-by, shutdown, interrupt — hidden with a sentence saying they need an API or agent target. With the database-backed history store the window's History page shows what the nodes ran. This is the shared-storage reach model a cluster behind a load balancer needs, with zero worker changes and no inbound port; the docs say what a window can and cannot do and that it needs the cluster's serializer configuration. (#​3772, #​3828)
  • The documentation compares, and says where Quartz loses. A comparison page puts Quartz.NET beside Hangfire 1.8.25, TickerQ 10.4.0, Wolverine 6.35 and Coravel 6.0, one sourced table per concern, with a section collecting the places Quartz is the more expensive answer; two guides map Hangfire's and TickerQ's APIs to ours and name the differences that bite (six cron fields and a different default time zone, [Queue] → execution groups that bound but do not route, retries held in the store rather than the worker, a dashboard that refuses to start until told who may reach it). The best-practices page no longer says Quartz has no retry policy, the quick start leads with the three-line form, and the index says what nobody knew was in the box. (#​3808, #​3813, #​3814)
  • The health check can say a scheduler has stopped firing. QuartzHealthCheckOptions.StaleFiringTolerance (off unless set; 3 is the documented starting value) asks the store for a schedulable trigger whose fire time passed more than that many misfire thresholds ago and reports Degraded, or Unhealthy past twice the bar — the silent stall that "running" and "store answers" never caught, with the overdue trigger, when it was due and by how much in the report's data. The worse of the check-in and stalled-firing verdicts now wins, where a late check-in used to hide a stall. Underneath, TriggerQuery.NextFireTimeBefore is a new filter every store and the HTTP API honour. (#​3809, #​3817)

Public API — additive only

Added What it is
[QuartzJob], [CronTrigger] the attributes the source generator reads; AddDeclaredJobs() is generated into your assembly
Continuation, ContinuationCondition, ExecutionOutcome, TriggeredJobCompleteContext, AwaitingContinuation a continuation, the outcomes that release it, what a firing did, and the context a store's completion receives
ITrigger.Continuation, ITriggerConfigurator<TJob>.StartAfter — default interface members; TriggerBuilder<TJob>.StartAfter; ScheduleJob<TJob, TInput>(input, Continuation after, options); JobChainingJobListener.AddJobChainLink(first, second, condition) and JobExecutionVetoed declaring a continuation, in the builder, in the one-off call, or on the recurring listener
IJobStore.FiringComplete(TriggeredJobCompleteContext) — DIM; IDriverDelegate.SelectAwaitingContinuations, ReleaseContinuation, ResetContinuationFireTime, SelectSchedulerNames — DIMs what a store and a dialect implement to settle continuations and to be discovered by a window; a store from outside this repository keeps working unchanged
TriggerState.Awaiting, StoredTriggerState.Awaiting; TriggerHeader.ContinuesAfter, ContinuationCondition; TriggerQuery.NextFireTimeBefore; the AdoConstants names of the new columns, tables and state the state on the wire and in the store, and the two new query filters
IJobExecutionContext.Outcome, RetryScheduled; ITriggerListener.TriggerRetriesExhausted — DIMs; ExecutionHistoryEntry.RetryAttempt, RetryScheduled; ExecutionHistoryQuery.FailedFinally; RetryPolicy.Exponential(…, jitter) and RetryPolicy.Jitter; the meter quartz.trigger.retries_exhausted the retry signal, end to end
IPersistentStoreBuilder.UseExecutionHistory() — DIM; AdoJobStoreOptions.ExecutionHistory history in the database
SchedulerOrigin.Window; SchedulerRegistration.Target; QuartzDashboardOptions.AttachStore, AttachStoreOptions; SchedulerHeaderDto.Target, IsWindow, DisplayName store-attached targets
QuartzHealthCheckOptions.StaleFiringTolerance the stalled-firing reading
IThreadPool.TryRunWithState — DIM the pool dispatch that carries state instead of a closure
ObjectDoesNotExistException what scheduling a continuation of a trigger the store does not hold throws, naming both keys; it round-trips over the HTTP API

Nothing was removed or reshaped. Baseline diff v4.1.1..v4.2.0: Quartz 111 added member lines, 0 removed, twelve of them default interface members; Quartz.Dashboard 12 added, 0 removed (24 lines re-ordered by the generator); Quartz.AspNetCore and Quartz.HttpClient unchanged. The Quartz package now depends on nothing new and carries analyzers/dotnet/cs/Quartz.Analyzers.dll and buildTransitive/net10.0/Quartz.targets, which reads DisableQuartzAnalyzers.

Schema

database/migrations/4.2/add_continuations_<dialect>.sql adds CONTINUES_TRIGGER_NAME, CONTINUES_TRIGGER_GROUP and CONTINUATION_CONDITION to QRTZ_TRIGGERS, nullable. Required from 4.2.0: a 4.2 node refuses to start against a database without them, naming the script. Safe to apply while 4.0 and 4.1 nodes run — they never read the columns — but a 4.1 node cannot settle a continuation, so migrate, roll every node, then start scheduling them. 4.2/add_execution_history_<dialect>.sql adds QRTZ_EXECUTION_HISTORY and QRTZ_MISFIRE_HISTORY, optional: needed only with UseExecutionHistory(); a fresh install and ProvisionSchema() create them regardless.

Behaviour changes worth noting

  • TriggerState.Awaiting is a new value on the wire; a 4.1 Quartz.HttpClient reading a 4.2 host that has continuations sees a state it does not know — upgrade the client. A store outside this repository that overrides TriggeredJobComplete keeps working (FiringComplete forwards to it) and settles no continuations until it implements the new member.
  • Quartz.nupkg carries an analyzer. A project whose cron or timeout literals were wrong builds no longer — that is the point — and a build with TreatWarningsAsErrors sees QZ0003 as an error; <DisableQuartzAnalyzers>true</DisableQuartzAnalyzers> opts out, .editorconfig re-tunes a severity.
  • A continuation needs its parent to exist when it is scheduled. Storing one whose parent trigger the store does not hold throws ObjectDoesNotExistException instead of waiting for ever. A scheduling file may still name a parent declared later in the same file — triggers are stored parent-first — but a parent in another file, or scheduled by a later call, has to come first.
  • A released continuation is an ordinary trigger. It forgets its parent (its ContinuesAfter reads null), waits behind its job when that job disallows concurrent execution and is running, honours its calendar and end time — one released past its end time is discarded — and a continuation that is discarded takes everything waiting on it with it.
  • A store-attached window cannot be started. Start() on one — from code or over the HTTP API, which also refuses standby and shutdown for it — throws before anything runs, where it used to run the cluster's start-up recovery against a live database. During a mixed 4.1/4.2 window, do not pause, resume or reschedule a continuation from a 4.1 node.
    ... (truncated)

Commits viewable in compare view.

Updated Radzen.Blazor from 11.4.2 to 11.4.3.

Release notes

Sourced from Radzen.Blazor's releases.

11.4.3

11.4.3 - 2026-09-24

Improvements

  • Spreadsheet custom cell editors work with the keyboard without relying on built-in editor internals: keys typed before the editor has focus are flushed into any input, textarea or contenteditable in it, the new SpreadsheetCellEditContext.Value holds the text being edited and is committed when the user moves to another cell, and Escape, Enter, Tab and Context.CommitAsync return focus to the grid instead of the page body. The custom cell types demo gains a text editor for the Status column and a section describing the editor contract.
  • Premium themes updated.

Fixes

  • DataGrid rows no longer render columns removed in the same render against the new data. Replacing the Columns fragment and Data together, for example a DataRow grid whose columns come from a settings list and whose DataTable changes shape, threw ArgumentException (Column 'COL2' does not belong to table) from the old columns' Template, because the dropped columns were disposed after the grid render had already handed them to the rows. Regression from 11.3.0.
  • Popup repositions when its content changes size while open. A popup opened above its anchor kept its top when the content shrank, for example when a RadzenTree inside it collapsed, and drifted away from the anchor; a popup opened below ran past the bottom of the viewport when its content grew. Smart-positioned popups now observe their size and flip above or below as space allows, as DropDown already did after filtering.
  • Spreadsheet reads cells past a worksheet's grid as empty cells, as Excel does. A loaded sheet was sized to its used range, so =SUM(A1:A100) or =A50 over data in A1:A3 evaluated to #REF! where Excel shows 6 and 0; a reference past Excel's last row or column is #NAME? and negative or deleted references stay #REF!. A range such as A2:XFD1048576 no longer takes minutes to register (#​2732).
  • Spreadsheet ROW() and COLUMN() read through another formula return the row and column of their own cell. Functions received the cell being recalculated instead of the cell whose formula they belong to, so with B1 =ROW() and A5 =B1, A5 showed 5 or 1 depending on recalculation order.
  • Spreadsheet evaluates operators and functions over ranges like Excel array formulas. Any arithmetic, comparison or concatenation with a range operand threw InvalidCastException, so =SUMPRODUCT((A1:A3>=2)B1:B3) or =INDEX(B1:B3,MATCH(1,(A1:A3=2)(B1:B3=20),0)) failed a recalculation or Workbook.LoadFromStream. Operators and single-value functions now apply to every cell with Excel's broadcasting, and a range used where one value is needed is implicitly intersected with the formula cell (#​2732).
  • Spreadsheet formulas that reference a cell whose formula does not parse return #NAME?. The syntax tree of such a cell was evaluated anyway, so a dependent of =SUM( threw InvalidOperationException and aborted a recalculation or Workbook.LoadFromStream (#​2732).
  • Spreadsheet loads xlsx hyperlinks whose ref is a range. Excel writes ref="J2:O2" for a link over merged or adjacent cells, which threw ArgumentException (Invalid A1 notation) and aborted Workbook.LoadFromStream; the link now applies to every cell of the range (#​2732).
  • Spreadsheet evaluates formulas that use an unquoted sheet or defined name outside ASCII, such as 入荷実績 or Übersicht. The lexer only started an identifier at an ASCII letter, so the cell showed #NAME? and any formula referencing it threw InvalidOperationException, which aborted Workbook.LoadFromStream (#​2732).
  • Spreadsheet clipboard shortcuts inside a cell editor or the formula bar no longer also act on the grid, so pasting into the editor does not paste into the selected cells and copying text inside it does not replace the clipboard with the cell selection. Keys typed in a text field of a cell editor stay in it instead of restarting type-to-edit, clearing the cell or undoing a grid action, and Enter during IME composition no longer commits.
  • Spreadsheet keeps typed and pasted input as literal text in cells with a Text number format, so 0071 stays 0071 and =1+1 stays text as in Excel; a leading apostrophe is still stripped and sets QuotePrefix. Undo of a cell edit restores a snapshot of the cell instead of replaying its old text.
  • Chart keeps its hover state consistent after a fast mouse exit. A mousemove queued by the Blazor Server throttle fired after the leave was sent, so the chart froze its last tooltip and ignored synced hovers from other charts in the same SyncGroup until the cursor re-entered it. Zoomed charts also no longer snap the tooltip, active point and synced marker to points scrolled out of view.
  • DropDown, ListBox and DropDownDataGrid re-filter when SearchText is changed programmatically. The parameter setter only stored the text, so the cached filtered view stayed in place and LoadData was never invoked with the new filter; setting the bound value to null left the list filtered (#​2731).

Commits viewable in compare view.

Updated WolverineFx.Marten from 6.39.1 to 6.40.0.

Release notes

Sourced from WolverineFx.Marten's releases.

6.40.0

51 commits since V6.39.1. A minor release rather than a patch, because this wave carries new capability and not only fixes.

New capability

  • Capacity-aware agent assignment (#​3959) — agent distribution now accounts for node capacity rather than treating every node as interchangeable. Landed as @​mlh758's #​4297, then hardened in #​4596 and #​4598 to require a real load monitor, stop a shed draining the cluster, and hold pins across every distribution path. Note that the node-load advertisement is PostgreSQL-only until #​4593 lands for the other stores.
  • External table transport for Oracle (#​4482) — implemented by @​Trasvi in #​4558, with follow-ups in #​4574, #​4577 and #​4578. The shared ExternalTableTransportCompliance suite now holds Oracle, PostgreSQL, SQL Server, MySQL and Sqlite to the same bar.
  • A deduplication claim now rides the business transaction on Marten (#​4505), Polecat (#​4570) and Fisher (#​4571). The claim is enlisted in the same unit of work as the handler's own writes instead of being committed on a connection of its own and compensated with a release. A refusal that never commits therefore never claims the id, and there is no release left to mis-order.

Behaviour changes

  • An unknown tenant id answers 404 ProblemDetails instead of 500 (#​4516).
  • Concurrency failures can be mapped to 409 with a one-line opt-in (#​4512).
  • MissingNamedConnectionStringsException stays an InvalidOperationException (#​4527), and every named connection string is now validated in one pass at startup.
  • Wolverine's Kafka error handler composes with the user's instead of replacing it (#​4522).

Exception message wave

#​4511–#​4532 — 18 PRs making the framework's exceptions name a remedy rather than only a symptom: saga failures, handler discovery, SNS/Rabbit MQ/Azure Service Bus/Redis/SignalR configuration problems, oversized messages, unreadable HTTP bodies, and the transports' previously message-less exceptions.

Fixes

  • Stop reading steady-state throughput as a stuck recovery batch, and detect a genuinely stuck outbox from the head of the queue (#​4499).
  • Sweep expired handled envelopes on Cosmos DB, and make DeleteAllHandledAsync work (#​4509).
  • Bound SQLite's two reaps (#​4567).
  • Only ever write ASP.NET Core's endpoint data sources from the composition thread (#​4500).
  • Grandfather a group-affinity candidate per member rather than per partition (#​4562) — @​erdtsieck's #​4563, with blue/green Polecat coverage in #​4576.
  • Declare a module's ancillary store once per namespace (#​4507) — @​uniquelau's #​4508.
  • Apply an ISendMyself published from a projection side effect, on all three stores (#​4556) — built on @​erdtsieck's #​4557.
  • Reject conflicting sending and listening modes on shared listeners (#​4059) — built on @​tmorejon's #​4506.
  • Say "disabled tenant" when that is what happened, and actually refuse one (#​4586).
  • Stop the Redis protocol version header surviving a round trip (#​4595).
  • Do not treat a sticky-bound listen-only endpoint as a local send target (#​4510).
  • Scope Polecat's and Fisher's duplicate-message Discard() to the inbox table (#​4565).

Dependencies

Unchanged from 6.39.1 — Marten 9.35.0, Polecat 5.29.0, Fisher 1.10.0, Weasel 9.32.0, JasperFx 2.74.0. This release ships exactly what its CI has been green against; the pin bump follows separately.

Contributors

Thank you to everyone who contributed code to this release:

Commits viewable in compare view.

Updated WolverineFx.RuntimeCompilation from 6.39.1 to 6.40.0.

Release notes

Sourced from WolverineFx.RuntimeCompilation's releases.

6.40.0

51 commits since V6.39.1. A minor release rather than a patch, because this wave carries new capability and not only fixes.

New capability

  • Capacity-aware agent assignment (#​3959) — agent distribution now accounts for node capacity rather than treating every node as interchangeable. Landed as @​mlh758's #​4297, then hardened in #​4596 and #​4598 to require a real load monitor, stop a shed draining the cluster, and hold pins across every distribution path. Note that the node-load advertisement is PostgreSQL-only until #​4593 lands for the other stores.
  • External table transport for Oracle (#​4482) — implemented by @​Trasvi in #​4558, with follow-ups in #​4574, #​4577 and #​4578. The shared ExternalTableTransportCompliance suite now holds Oracle, PostgreSQL, SQL Server, MySQL and Sqlite to the same bar.
  • A deduplication claim now rides the business transaction on Marten (#​4505), Polecat (#​4570) and Fisher (#​4571). The claim is enlisted in the same unit of work as the handler's own writes instead of being committed on a connection of its own and compensated with a release. A refusal that never commits therefore never claims the id, and there is no release left to mis-order.

Behaviour changes

  • An unknown tenant id answers 404 ProblemDetails instead of 500 (#​4516).
  • Concurrency failures can be mapped to 409 with a one-line opt-in (#​4512).
  • MissingNamedConnectionStringsException stays an InvalidOperationException (#​4527), and every named connection string is now validated in one pass at startup.
  • Wolverine's Kafka error handler composes with the user's instead of replacing it (#​4522).

Exception message wave

#​4511–#​4532 — 18 PRs making the framework's exceptions name a remedy rather than only a symptom: saga failures, handler discovery, SNS/Rabbit MQ/Azure Service Bus/Redis/SignalR configuration problems, oversized messages, unreadable HTTP bodies, and the transports' previously message-less exceptions.

Fixes

  • Stop reading steady-state throughput as a stuck recovery batch, and detect a genuinely stuck outbox from the head of the queue (#​4499).
  • Sweep expired handled envelopes on Cosmos DB, and make DeleteAllHandledAsync work (#​4509).
  • Bound SQLite's two reaps (#​4567).
  • Only ever write ASP.NET Core's endpoint data sources from the composition thread (#​4500).
  • Grandfather a group-affinity candidate per member rather than per partition (#​4562) — @​erdtsieck's #​4563, with blue/green Polecat coverage in #​4576.
  • Declare a module's ancillary store once per namespace (#​4507) — @​uniquelau's #​4508.
  • Apply an ISendMyself published from a projection side effect, on all three stores (#​4556) — built on @​erdtsieck's #​4557.
  • Reject conflicting sending and listening modes on shared listeners (#​4059) — built on @​tmorejon's #​4506.
  • Say "disabled tenant" when that is what happened, and actually refuse one (#​4586).
  • Stop the Redis protocol version header surviving a round trip (#​4595).
  • Do not treat a sticky-bound listen-only endpoint as a local send target (#​4510).
  • Scope Polecat's and Fisher's duplicate-message Discard() to the inbox table (#​4565).

Dependencies

Unchanged from 6.39.1 — Marten 9.35.0, Polecat 5.29.0, Fisher 1.10.0, Weasel 9.32.0, JasperFx 2.74.0. This release ships exactly what its CI has been green against; the pin bump follows separately.

Contributors

Thank you to everyone who contributed code to this release:

Commits viewable in compare view.

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore <dependency name> major version will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself)
  • @dependabot ignore <dependency name> minor version will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself)
  • @dependabot ignore <dependency name> will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself)
  • @dependabot unignore <dependency name> will remove all of the ignore conditions of the specified dependency
  • @dependabot unignore <dependency name> <ignore condition> will remove the ignore condition of the specified dependency and ignore conditions

Bumps Marten from 9.39.0 to 9.39.1
Bumps Quartz.Extensions.Hosting from 4.1.1 to 4.2.0
Bumps Quartz.Serialization.SystemTextJson from 4.1.1 to 4.2.0
Bumps Radzen.Blazor from 11.4.2 to 11.4.3
Bumps WolverineFx.Marten from 6.39.1 to 6.40.0
Bumps WolverineFx.RuntimeCompilation from 6.39.1 to 6.40.0

---
updated-dependencies:
- dependency-name: Marten
  dependency-version: 9.39.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nuget-minor-patch
- dependency-name: Quartz.Extensions.Hosting
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: nuget-minor-patch
- dependency-name: Quartz.Serialization.SystemTextJson
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: nuget-minor-patch
- dependency-name: Radzen.Blazor
  dependency-version: 11.4.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nuget-minor-patch
- dependency-name: WolverineFx.Marten
  dependency-version: 6.40.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: nuget-minor-patch
- dependency-name: WolverineFx.RuntimeCompilation
  dependency-version: 6.40.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: nuget-minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added .NET Pull requests that update .NET code dependencies Pull requests that update a dependency file labels Sep 26, 2026
@andregoepel

Copy link
Copy Markdown
Owner

Superseded by merged dependency updates #223, #226 and #227. Main already contains Marten 9.45.0, Wolverine 6.45.0, Quartz 4.3.0 and Radzen.Blazor 11.5.1. The extra direct Quartz reference added to the UI project by lockfile synchronization is outside this dependency update; Quartz remains referenced by the Hosting project.

@andregoepel andregoepel closed this Oct 2, 2026
@dependabot @github

dependabot Bot commented on behalf of github Oct 2, 2026

Copy link
Copy Markdown
Contributor Author

This pull request was built based on a group rule. Closing it will not ignore any of these versions in future pull requests.

To ignore these dependencies, configure ignore rules in dependabot.yml

@dependabot
dependabot Bot deleted the dependabot/nuget/nuget-minor-patch-565cbd0284 branch October 2, 2026 11:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file .NET Pull requests that update .NET code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant