Bump the nuget-dependencies group with 22 updates - #58
Open
dependabot[bot] wants to merge 1 commit into
Open
Conversation
Bumps Injectio from 6.1.0 to 6.2.0 Bumps Microsoft.AspNetCore.OpenApi from 10.0.9 to 10.0.11 Bumps Microsoft.CodeAnalysis.Analyzers from 5.6.0 to 5.9.0 Bumps Microsoft.CodeAnalysis.Common from 5.6.0 to 5.9.0 Bumps Microsoft.CodeAnalysis.CSharp from 5.6.0 to 5.9.0 Bumps Microsoft.Extensions.Hosting.Abstractions from 10.0.9 to 10.0.11 Bumps Microsoft.Extensions.Http from 10.0.9 to 10.0.11 Bumps Microsoft.Extensions.Logging from 10.0.9 to 10.0.11 Bumps NLog from 6.1.4 to 6.2.0 Bumps NLog.Extensions.Logging from 6.1.4 to 6.2.0 Bumps NLog.Web.AspNetCore from 6.1.4 to 6.2.0 Bumps NRedisStack from 1.6.0 to 1.7.4 Bumps Quartz from 3.18.2 to 3.20.0 Bumps Quartz.AspNetCore from 3.18.2 to 3.20.0 Bumps Scalar.AspNetCore from 2.16.11 to 2.17.2 Bumps SkiaSharp from 4.150.0 to 4.151.1 Bumps SkiaSharp.NativeAssets.Linux.NoDependencies from 4.150.0 to 4.151.1 Bumps SkiaSharp.NativeAssets.macOS from 4.150.0 to 4.151.1 Bumps SkiaSharp.NativeAssets.Win32 from 4.150.0 to 4.151.1 Bumps SqlSugarCore from 5.1.4.216 to 5.1.4.217 Bumps StackExchange.Redis from 3.0.17 to 3.1.31 Bumps Telegram.Bot from 22.10.1.1 to 22.10.3 --- updated-dependencies: - dependency-name: Injectio dependency-version: 6.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: nuget-dependencies - dependency-name: Microsoft.AspNetCore.OpenApi dependency-version: 10.0.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: nuget-dependencies - dependency-name: Microsoft.CodeAnalysis.Analyzers dependency-version: 5.9.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: nuget-dependencies - dependency-name: Microsoft.CodeAnalysis.Common dependency-version: 5.9.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: nuget-dependencies - dependency-name: Microsoft.CodeAnalysis.CSharp dependency-version: 5.9.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: nuget-dependencies - dependency-name: Microsoft.Extensions.Hosting.Abstractions dependency-version: 10.0.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: nuget-dependencies - dependency-name: Microsoft.Extensions.Http dependency-version: 10.0.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: nuget-dependencies - dependency-name: Microsoft.Extensions.Logging dependency-version: 10.0.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: nuget-dependencies - dependency-name: NLog dependency-version: 6.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: nuget-dependencies - dependency-name: NLog.Extensions.Logging dependency-version: 6.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: nuget-dependencies - dependency-name: NLog.Web.AspNetCore dependency-version: 6.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: nuget-dependencies - dependency-name: NRedisStack dependency-version: 1.7.4 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: nuget-dependencies - dependency-name: StackExchange.Redis dependency-version: 3.1.31 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: nuget-dependencies - dependency-name: Quartz dependency-version: 3.20.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: nuget-dependencies - dependency-name: Quartz.AspNetCore dependency-version: 3.20.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: nuget-dependencies - dependency-name: Scalar.AspNetCore dependency-version: 2.17.2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: nuget-dependencies - dependency-name: SkiaSharp dependency-version: 4.151.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: nuget-dependencies - dependency-name: SkiaSharp.NativeAssets.macOS dependency-version: 4.151.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: nuget-dependencies - dependency-name: SkiaSharp.NativeAssets.Win32 dependency-version: 4.151.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: nuget-dependencies - dependency-name: SkiaSharp.NativeAssets.Linux.NoDependencies dependency-version: 4.151.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: nuget-dependencies - dependency-name: SqlSugarCore dependency-version: 5.1.4.217 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: nuget-dependencies - dependency-name: Telegram.Bot dependency-version: 22.10.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: nuget-dependencies ... Signed-off-by: dependabot[bot] <support@github.com>
This was referenced Aug 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Updated Injectio from 6.1.0 to 6.2.0.
Release notes
Sourced from Injectio's releases.
6.2.0
What's Changed
Full Changelog: loresoft/Injectio@v6.1.0...v6.2.0
6.2.0-beta.1
Full Changelog: loresoft/Injectio@v6.1.0...v6.2.0-beta.1
Commits viewable in compare view.
Updated Microsoft.AspNetCore.OpenApi from 10.0.9 to 10.0.11.
Release notes
Sourced from Microsoft.AspNetCore.OpenApi's releases.
No release notes found for this version range.
Commits viewable in compare view.
Updated Microsoft.CodeAnalysis.Analyzers from 5.6.0 to 5.9.0.
Release notes
Sourced from Microsoft.CodeAnalysis.Analyzers's releases.
No release notes found for this version range.
Commits viewable in compare view.
Updated Microsoft.CodeAnalysis.Common from 5.6.0 to 5.9.0.
Updated Microsoft.CodeAnalysis.CSharp from 5.6.0 to 5.9.0.
Updated Microsoft.Extensions.Hosting.Abstractions from 10.0.9 to 10.0.11.
Release notes
Sourced from Microsoft.Extensions.Hosting.Abstractions's releases.
No release notes found for this version range.
Commits viewable in compare view.
Updated Microsoft.Extensions.Http from 10.0.9 to 10.0.11.
Release notes
Sourced from Microsoft.Extensions.Http's releases.
No release notes found for this version range.
Commits viewable in compare view.
Updated Microsoft.Extensions.Logging from 10.0.9 to 10.0.11.
Release notes
Sourced from Microsoft.Extensions.Logging's releases.
No release notes found for this version range.
Commits viewable in compare view.
Updated NLog from 6.1.4 to 6.2.0.
Release notes
Sourced from NLog's releases.
6.2
Improvements
Release notes for NLog v6.2: https://nlog-project.org/2026/08/16/nlog-6-2-aot-build-size.html
Commits viewable in compare view.
Updated NLog.Extensions.Logging from 6.1.4 to 6.2.0.
Release notes
Sourced from NLog.Extensions.Logging's releases.
6.2
Release notes for NLog v6.2: https://nlog-project.org/2026/08/16/nlog-6-2-aot-build-size.html
Commits viewable in compare view.
Updated NLog.Web.AspNetCore from 6.1.4 to 6.2.0.
Release notes
Sourced from NLog.Web.AspNetCore's releases.
6.2
Release notes for NLog v6.2: https://nlog-project.org/2026/08/16/nlog-6-2-aot-build-size.html
Commits viewable in compare view.
Updated NRedisStack from 1.6.0 to 1.7.4.
Release notes
Sourced from NRedisStack's releases.
No release notes found for this version range.
Commits viewable in compare view.
Updated Quartz from 3.18.2 to 3.20.0.
Release notes
Sourced from Quartz's releases.
3.20.0
Quartz.NET 3.20.0 is a feature release: the ADO.NET job store can take part in the application's own database transaction, job instantiation failures finally carry the trigger they died on, and the daylight-saving and calendar arithmetic got a systematic pass that fixed several defects a schedule can actually hit. The public API is additive only — three new members and one new exception type, nothing changed or removed — but this is not quite a drop-in upgrade. Four things to read before you take it:
database/that used to sit flat is nowdatabase/migrations/<version>/<name>_<dialect>.sql, one runnable file per database instead of one file with five commented-out dialect blocks. Old links still resolve against release tags; the mapping is below.database/migrations/3.20/). Nothing needs it to run, but PostgreSQL users should read that bullet.Highlights
quartz.jobStore.acceptEnlistedTransactionsorAcceptEnlistedTransactions(), then hand the store a connection for the duration of a scope withIScheduler.EnlistTransaction(DbTransaction)orIScheduler.EnlistConnection(DbConnection). The application owns the commit; the job store neither commits nor rolls back, and the enlistment flows with the current asynchronous context the wayTransaction.Currentdoes — which is what makes it work whileIJobStoreis a singleton and aDbContextis scoped. Nothing about it is EF Core specific. Taking part always means handing over a connection: an ambientTransactionScopealone is not enough, because a connection the job store opens for itself would be a second connection in that transaction and would force promotion to a distributed one — which needs MSDTC (Windows only, and on modern .NET also an explicit opt-in throughTransactionManager.ImplicitDistributedTransactions) and is impossible on Npgsql, which has no distributed transaction support at all. Sharing the one connection is what keeps the transaction local. Opt-in, because joining the application's transaction changes when, and whether, scheduling commits;JobStoreCMTis untouched, since running inside a container-managed transaction is that store's whole contract. (#3204, fixes #2038)txIsolationLevelSerializableapplies only to the job store's own connections.IJobFactorycannot produce a job the trigger has already fired and been committed, but there is noIJobExecutionContextyet, so no trigger or job listener can be raised andISchedulerListener.SchedulerErroris the only notification. It carried the job key as interpolated message text and nothing else, leaving callers parsing a string to find out which firing died. The newQuartz.Core.JobInstantiationException : SchedulerExceptioncarriesTrigger,JobDetailandFireInstanceId— the same shapeJobExecutionProcessExceptionhas for execution-time failures — and both catch blocks inJobRunShell.Runraise it, so the DI and non-DI paths are enriched alike. Message text is byte-identical in both paths, including a long-standing misplaced quote, so anything parsing it today keeps working while it migrates. (#3215, closes #3213)SchedulerExceptionpath the exception handed to listeners is now aJobInstantiationExceptionwrapping the factory's exception rather than being it; the original is reachable asInnerException. The choice betweenNoInstructionandSetAllJobTriggersErrorstill tests the original exception, so cancellation and disposal races behave exactly as before.TimeZoneInfo.GetUtcOffsetreturning the pre-gap offset for positive-delta zones. It is now two internalTimeZoneUtilhelpers —ResolveLocal(ambiguous → the first/daylight occurrence; nonexistent → paired with the pre-gap offset, found by scanning backwards a minute at a time so 30-minute deltas and negative-delta zone models work) andWalkToGapEnd— with all five call-site families migrated onto them. Two defects fell out: a negative-daylight-delta gap hazard, where a zone modelled with a negative delta (Europe/Dublin on TZif data, so Linux and macOS) produced an instant before the gap — a scheduler hot-loop hazard the ambiguity demotion does not cover; and a sub-second demotion defect, whereCronExpression.GetTimeAftercompared its whole-second candidate against the untruncated after-time, so a sub-second after-time inside the repeated fall-back hour demoted a valid fire a whole hour forward. The migrations are behaviour-neutral for every positive-delta zone, guarded by a minute-by-minute differential test across ±3 hours of all eight test zones' transitions. (#3198, building on #3195)GetTimeAftermonotonic in its argument again, and stopsGetTimeBeforereturning instants the expression never fires at. Fire times inside the repeated fall-back hour can therefore differ from 3.19.DailyTimeIntervalTriggerno longer fires past itsEndTimeUtc— the day-of-week advance resolved the advanced day's start-of-day through the instant-based offset overload while every sibling call site used the wall-clock policy overload, so the offset carried through theAddDayswalk was stale once the walk crossed a transition, and an in-gap start-of-day resolved one transition delta too early. TheEndTimeUtccheck inside the same method consumed the mis-resolved instant before the downstream rescue corrected it, so with an end time between the wrong instant and the right one the trigger fired once past its configured end. Only triggers with a partialDaysOfWeekset were affected — the existing tests all use a full week, where the two resolutions coincide. (#3196)HolidayCalendar,AnnualCalendar,MonthlyCalendarandWeeklyCalendarbuilt the boundary they walk as midnight at the offset the queried instant happened to carry, and advanced it withAddDays, which keeps that offset. Both assume a local day begins at midnight at the same offset the rest of the day carries, and a transition day is exactly the day where it does not. In America/Santiago, where the clocks move at midnight, the 23-hour day (2019-09-08) answered with the excluded instant it was asked about, and the 25-hour day (2019-04-06) overshot by 23 hours; in a zone that moves its clocks later in the day the answer lands a whole date out (Europe/Helsinki on 2024-03-31, asked from the afternoon, answered 2024-03-30); and a run of excluded days crossing a transition drifts by the transition delta. The boundary now resolves local midnight by naming the date rather than by adding twenty-four hours.CronCalendarnever had the shape. (#3478, fixes #3457)DailyCalendar.GetNextIncludedTimeUtcis jumped to rather than walked up to — it named the window's edges on the date the argument fell on and paired them with the argument's offset, whileIsTimeIncludedconverts into the calendar's zone first, so a question asked in UTC about a calendar keeping another zone's hours had the two disagree. The jump then landed on a window an offset away from the one being tested, the loop fell through to its last resort — one precision step at a time, bounded only by where the wrong window happened to end — and it could step clean over an included stretch. The measured reproduction, an inverted 21:00–22:00 America/Santiago calendar asked at2019-04-07T01:30Z:2019-09-09T00:00:59.999Z, five months late, reached a minute of wall clock at a time, against2019-04-08T01:00Zin two passes now. The whole daily-calendar half of the new test fixture takes 1 m 52 s against the unfixed file and milliseconds against this one. The answer is now named from the window's own edges — the instant the day opens at, the second reading of that edge where the clock repeats it, the instant the clocks moved where a fall-back takes the clock back to before the window opened, and the day's own first instant — each checked against the calendar's own rule before it is taken. (#3478, fixes #3466)precisionStepMillisoptimisation (#2285) is removed, because a jump has nothing left to speed up and it was rounding the answer up by as much as a minute.GetNextIncludedTimeUtcanswers are now exact: a06:00–22:00calendar asked at21:59Zreturns22:00:00.001Zwhere it returned22:01:00.000Z, and asking about an instant the calendar already includes gives back the next millisecond rather than the next minute. TheNestedCalendarTestsperformance test that came with #2285 is unchanged and passes.GetTimeRangeStartingTimeUtcandGetTimeRangeEndingTimeUtcare public and carried the same offset assumption in public — asked in UTC about a Santiago calendar they answered about the UTC date at+00:00, which no caller can use. They now read the date of the local day the instant falls in. For a calendar left on the defaultTimeZoneInfo.Localasked with a local value, the conversion is a no-op. No signature changed.UseNewtonsoftJsonSerializerleavesRegisterTriggerConvertersoff by default, and with it off Json.NET's default contract wrote aTimeZoneInfoas its whole public surface (Id,DisplayName,BaseUtcOffset, all read-only), so reading it back set nothing and the trigger's getter fell through toTimeZoneInfo.Local. A trigger stored under Tokyo fired on whichever zone the reading machine was in, silently. Measured before the fix:CalendarIntervalTriggerImpl,RecurrenceTriggerImplandCronTriggerImplall came back on the reading machine's zone. An internalTimeZoneInfoConverterwrites the id and reads both the id and the old object form, attached per property to members typed as aTimeZoneInfo— deliberately not on the serializer's converter list, since that list is consulted for a value's runtime type wherever it appears and a zone held in a job data map value would then lose the$typethat path carries. The four privatetimeZoneInfoIdhelpers that were meant for this and never worked (DefaultContractResolverdoes not serialize private members) are gone.BLOB_TRIGGERSpayloads written byBinaryObjectSerializerare unaffected: they are computed properties, so there was never a backing field forBinaryFormatterto match. (#3505)DailyTimeIntervalTriggerImplcannot be read back at all —TimeOfDayhas no parameterless constructor, so Json.NET fails loudly onEndTimeOfDay. It predates this change, it fails rather than corrupting, and the ADO store reaches that path only for a trigger it serializes as a blob, so a shippedDailyTimeIntervalTriggerdoes not go through it.JobInterruptMonitorPlugininterrupted by job key, so a monitor that elapsed cancelled every running execution of that job rather than the one it was watching. Worse, a vetoed fire's monitor was never cancelled at all, because the only cleanup point wasTriggerComplete, which a vetoed fire never reaches — so every veto leaked a live monitor that would later interrupt an unrelated, healthy execution of the same job. It now callsInterrupt(fireInstanceId), cancels the monitor on veto through a job listener of its own, does not start a second monitor for a re-executed job sharing a fire instance id, and removes its own bookkeeping entry when it elapses. Both scenarios are exactly as analysed in the report. (#3249, fixes #3248)IScheduler.Interrupt(fireInstanceId)now raisesISchedulerListener.JobInterrupted, matching theInterrupt(JobKey)overload — relevant to anyone calling the fire-instance overload directly. AndAutoInterruptableis now read from the merged job data map, consistent withMaxRunTime, so a trigger's data map can opt a fire in or out of auto-interruption rather than only override the timeout.CALENDAR_NAMEwas written as''rather thanNULLstopped firing entirely: every job store gates its calendar lookup onCalendarName is not null, so the empty string passed the gate, the lookup found nothing, and the fire was silently dropped.AbstractTrigger.CalendarNamenow stores a blank name asnull, without trimming — the name is a lookup key against whateverAddCalendarstored, so trimming" holidays "would break a calendar registered with padding and create the same bug from the other direction. That one setter is the choke point forTriggerBuilder, both JSON converters and the ADO read-back, so databases already holding''self-heal: the row rehydrates as "no calendar", the trigger fires again on the next acquisition, and the column is written back asNULLnext time it is persisted. No migration script.TriggerDetailsUpdate.WithCalendarNamenormalizes separately, since both stores check the calendar exists before the value reaches the trigger setter. Both stores now also log when they skip a fire over a missing calendar, which turns this class of report from a mystery into a one-line diagnosis. The dashboard, which produced the empty string by rebuilding the trigger field by field out of its display projection, now edits the trigger JSON it was already handed — which also preserves the node pin and the real trigger type it used to drop. (#3295, fixes #3294)CronCalendar.GetNextIncludedTimeUtcnever terminated from an excluded instant (it advanced withGetNextValidTimeAfter, which lands on another excluded time);CronCalendar's three-argument constructor dropped itstimeZone, so the calendar evaluated its exclusion cron in machine-local time; transient-error classification short-circuited onDbException.IsTransient, which on any .NET 6+ host matches every driver exception — so the deadlock-1205 list, the SQLite busy/locked check and the timeout fallback were all dead code and retryable failures were classified permanent; a prefix trigger-group pause paused only the first matching group and a prefix resume could never clear what a prefix pause recorded;JobInterruptMonitorPluginsilently ignored a numericMaxRunTime, falling back to the five-minute default with no log; and Newtonsoft deserialization populated read-only collections through their getters, so on the default configuration aDaysOfWeeksubset came back as all seven days. (#3334)DaysOfWeeksubset survives the plain Newtonsoft round trip. Deliberately not backported: the 4.x store-parity semantic alignments (pause-over-Error,ResumeAllmarker clearing, sentinel visibility, exception re-wrap types), which change observable maintenance-branch behaviour.SelectInstancesFiredTriggerRecordswas the only fired-trigger reader that never readPRIORITYandClusterRecoverassigns from exactly that reader, so every recovery trigger ran at priority 0, below the default 5, deprioritising recovery work precisely when a node has died. FourIDriverDelegatemembers failed on every provider because their bound parameter names never matched their SQL; nothing in the scheduler calls them, which is why it never surfaced, but they are public surface reachable fromJobStoreSupportsubclasses. And group matcher values are now escaped, with!as the escape character becauseESCAPE '\'is a MySQL syntax error while!is a plain literal on all six supported databases. (#3202)%or_now match literally in group matcher queries. Previously those characters acted as LIKE wildcards, so a matcher could list, pause, resume or delete groups it was not meant to match. The same correction meansResumeAlldeletes only the_$_ALL_GROUPS_PAUSED_$_sentinel row rather than a pattern in which all eight underscores were single-character wildcards.sched_name, which every Quartz statement filters on first, andidx_qrtz_t_nft_sthad its columns reversed —(next_fire_time, trigger_state)against an acquire query that is two equalities then a range, so the index could never bound the scan on state or scheduler name. This is the shape behind slow-acquire-on-PostgreSQL reports. Verified on live PostgreSQL 16: the upgrade script and a fresh install converge on byte-identicalpg_indexessets, and a 160,000-trigger acquire-plan comparison moves all three predicates from post-filter into the index condition — buffer hits 136 → 57, discarded rows 61 → 0. Across the other dialects, indexes whose columns are a leftmost prefix of a wider same-table index are dropped, with the coverer named on every drop.IDX_QRTZ_J_GRPdeliberately stays on 3.x, unlike 4.x, because its 4.x coverer does not exist here and it serves the group listings. The migration is optional and performance-only —database/migrations/3.20/index_alignment_<dialect>.sql. (#3203)database/migrations/<version>/<name>_<dialect>.sql, one directly-runnable file per database instead of one file carrying five commented-out dialect blocks, with every statement guarded so re-running is a no-op (SQLiteADD COLUMNis the documented exception — SQLite has no conditional DDL). The scripts are generated from one description in the build, because six hand-written dialect variants is how they drift, andVerifyMigrationsfails a checked-in script that no longer matches.database/README.mdis the new index: run order, per-version status, and the old-path → new-path map reproduced below. The migrations previously had no test coverage at all;MigrationScriptTestnow builds a 3.16-era schema from a checked-in baseline, applies 3.17 → 3.18 → 3.19 → 3.20 in order, applies each twice so the guards are exercised, and asserts the result matches what the currenttables_<dialect>.sqlproduces, table for table, column for column, index for index — on all six databases. It caught two real defects on the way: PostgreSQL's index alignment usedCREATE INDEX IF NOT EXISTSfor three indexes that already exist in a 3.16 schema under the same name with different columns, so the guard silently kept the wrong shape — includingidx_qrtz_t_nft_st; and MySQL'sQRTZ_BLOB_TRIGGERScarried an InnoDB-auto-named inline index duplicating its primary key that the migration had left in place. It also backports the 2.5→2.6QRTZ_CRON_TRIGGERS.TIME_ZONE_IDfix (#1985), which never reached this branch. (#3219, fixes #3218)main, and this branch links to them — they were briefly mirrored here, and a mirror goes stale silently every time 4.x's schema moves. A confidently wrong upgrade script is worse than one that is plainly somewhere else, sodatabase/migrations/4.0/is not on this branch anddatabase/README.mdpoints at https://github.com/quartznet/quartznet/tree/main/database/migrations/4.0 in every place it used to link to the folder. (#3373, and #3326, which stopped the SQLite 4.0 upgrade claiming it can be re-run when its fiveADD COLUMNs are unguarded)git diff(#3226). TheUnitTesttarget now asks each project which frameworks it declares and runs it once per framework:dotnet test --framework Xagainst a project that does not target X exits 0 having run nothing, so the Ubuntu and macOS legs had been running 116 tests and silently skipping the 1,753 inQuartz.Tests.Unitwhile the green check said otherwise (#3228). CI was unbroken by moving to Testcontainers 4.14.0, which references a patchedSSH.NET, and by pinningNuGet.Frameworksforward for SDK 10.0.400 (#3278). The build orchestrator moved to Fallout 10.4.0 (#3252),changelog.mdwas retired in favour of the GitHub releases that had already superseded it (#3224),sonar-project.propertieswas removed because SonarCloud's automatic analysis never read it (#3221), and a test that had been writing files into the application directory on every CI run stopped (#3293).Public API
Additive only. No existing signature changed, nothing was removed, and the new baselines are byte-identical across every other change in this release.
Quartz.Core.JobInstantiationException : SchedulerException, carryingTrigger,JobDetailandFireInstanceId(#3215)IScheduler.EnlistTransaction(DbTransaction)andIScheduler.EnlistConnection(DbConnection), plusAcceptEnlistedTransactions()on the persistent store builder and thequartz.jobStore.acceptEnlistedTransactionskey (#3204)Where the database scripts moved
The scripts used to sit flat in
database/, with the dialects other than SQL Server commented out inside each file. Those paths are gone from the branch tip. Old links keep working against release tags — for examplehttps://github.com/quartznet/quartznet/blob/v3.19.1/database/schema_30_add_preferred_node.sql.database/sqlserver_schema_10_to_20_upgrade.sqldatabase/schema_10_to_20_upgrade.sqlmigrations/2.0/schema_10_to_20_upgrade_sqlServer.sqldatabase/schema_20_to_22_upgrade.sqlmigrations/2.2/schema_20_to_22_upgrade_<db>.sqldatabase/schema_25_to_26_upgrade.sqlmigrations/2.6/schema_25_to_26_upgrade_<db>.sql3.19.1
Quartz.NET 3.19.1 is a small bug fix release with two targeted fixes:
DailyTimeIntervalTriggerno longer gets stuck in an infinite fire loop on DST spring-forward days, andStdSchedulerFactory.GetScheduler(schedName)now creates the scheduler when the name asked for is its own. There are no API or schema changes, so it is a drop-in upgrade from 3.19.0.Highlights
DailyTimeIntervalTriggerno longer spins on DST transition days —GetFireTimeAftercould return a time at or before the one it was given, which makesQuartzSchedulerThreadfire the trigger, compute the same next fire time, and fire again — pinning a CPU core and flooding the log. Two independent causes, both on a spring-forward day: the DST correction added for #1114 was applied to every interval size and in either direction (so every interval of an hour or less was affected, in every DST time zone), and the daily rollover toStartTimeOfDayreused whatever UTC offset the previous fire time carried (so in time zones that move the clock at midnight, such as Chile,StartTimeOfDay00:00 resolved to an instant before the transition — the same instant that was passed in). Verified across 3024 combinations of 12 time zones, both transitions, 21 intervals and 6 start times: 468 combinations produced non-advancing fire times before, none do now. (#3190, fixes #332)StdSchedulerFactory.GetScheduler(schedName)creates its own scheduler — asking a factory for the scheduler it is configured to produce returnednulluntil somebody had calledGetScheduler()first. It now creates it. Any other name stays a pure lookup, so probing for a scheduler somebody else owns still has no side effects, and the name comparison is case-insensitive to match howSchedulerRepositoryindexes names. The DI factory has behaved this way since #2845; this brings the property-configured factory in line. (#3188, reported in #2786, originally proposed in #360)What's Changed
Full Changelog: quartznet/quartznet@v3.19.0...v3.19.1
3.19.0
Quartz.NET 3.19.0 is a feature release: it adds node affinity for clustered scheduling, a fluent cron-expression builder, and richer
L/LWday-of-month expressions, plus clock-jump resilience and a modernized build and publishing pipeline. The public API is unchanged (all additions are additive), so it is a drop-in upgrade — with two things to note: the new node-affinity columns are an optional schema migration (the feature degrades gracefully without them), and a handful of previously-brokenL/LW/Wcron expressions now fire correctly (see below).Highlights
TriggerBuilder.WithPreferredNode(...); the node is preferred for acquisition but the trigger is still stolen on failover so it is never stranded if that node goes down. Adds optionalPREFERRED_NODE/PREFERRED_NODE_AUTOcolumns for ADO.NET job stores (database/schema_30_add_preferred_node.sql); when the columns are absent the scheduler logs a warning and behaves exactly as before. (#3013, #3144)CronExpressionBuilder— compose cron expressions programmatically, one field at a time, instead of hand-writing the string — handy when a schedule is assembled from user input such as a scheduling UI. (#3139)LandLWcombinable with other day-of-month values — the day-of-month field now accepts expressions such as1,15,Land the newLW-n/L-nWgrammar. This also corrects several previously-buggy edge cases:29W/31Wno longer silently skip short months,L-30Wno longer throws mid-schedule, and1,15Wnow appliesWto each day rather than only the first. These corrections change the fire times of a few expressions that were previously broken — review any storedL/LW/Wday-of-month expressions. (#2759)Note for
CronScheduleBuilderusers:AtHourAndMinuteOnGivenDaysOfWeek/WeeklyOnDayAndHourAndMinutenow emit textual day-of-week names (e.g.MON,WEDrather than2,4). The schedules are identical, but the generatedCRON_EXPRESSIONstring differs — relevant only if you compare stored cron strings byte-for-byte.What's Changed
Full Changelog: quartznet/quartznet@v3.18.2...v3.19.0
Commits viewable in compare view.
Updated Quartz.AspNetCore from 3.18.2 to 3.20.0.
Release notes
Sourced from Quartz.AspNetCore's releases.
3.20.0
Quartz.NET 3.20.0 is a feature release: the ADO.NET job store can take part in the application's own database transaction, job instantiation failures finally carry the trigger they died on, and the daylight-saving and calendar arithmetic got a systematic pass that fixed several defects a schedule can actually hit. The public API is additive only — three new members and one new exception type, nothing changed or removed — but this is not quite a drop-in upgrade. Four things to read before you take it:
database/that used to sit flat is nowdatabase/migrations/<version>/<name>_<dialect>.sql, one runnable file per database instead of one file with five commented-out dialect blocks. Old links still resolve against release tags; the mapping is below.database/migrations/3.20/). Nothing needs it to run, but PostgreSQL users should read that bullet.Highlights
quartz.jobStore.acceptEnlistedTransactionsorAcceptEnlistedTransactions(), then hand the store a connection for the duration of a scope withIScheduler.EnlistTransaction(DbTransaction)orIScheduler.EnlistConnection(DbConnection). The application owns the commit; the job store neither commits nor rolls back, and the enlistment flows with the current asynchronous context the wayTransaction.Currentdoes — which is what makes it work whileIJobStoreis a singleton and aDbContextis scoped. Nothing about it is EF Core specific. Taking part always means handing over a connection: an ambientTransactionScopealone is not enough, because a connection the job store opens for itself would be a second connection in that transaction and would force promotion to a distributed one — which needs MSDTC (Windows only, and on modern .NET also an explicit opt-in throughTransactionManager.ImplicitDistributedTransactions) and is impossible on Npgsql, which has no distributed transaction support at all. Sharing the one connection is what keeps the transaction local. Opt-in, because joining the application's transaction changes when, and whether, scheduling commits;JobStoreCMTis untouched, since running inside a container-managed transaction is that store's whole contract. (#3204, fixes #2038)txIsolationLevelSerializableapplies only to the job store's own connections.IJobFactorycannot produce a job the trigger has already fired and been committed, but there is noIJobExecutionContextyet, so no trigger or job listener can be raised andISchedulerListener.SchedulerErroris the only notification. It carried the job key as interpolated message text and nothing else, leaving callers parsing a string to find out which firing died. The newQuartz.Core.JobInstantiationException : SchedulerExceptioncarriesTrigger,JobDetailandFireInstanceId— the same shapeJobExecutionProcessExceptionhas for execution-time failures — and both catch blocks inJobRunShell.Runraise it, so the DI and non-DI paths are enriched alike. Message text is byte-identical in both paths, including a long-standing misplaced quote, so anything parsing it today keeps working while it migrates. (#3215, closes #3213)SchedulerExceptionpath the exception handed to listeners is now aJobInstantiationExceptionwrapping the factory's exception rather than being it; the original is reachable asInnerException. The choice betweenNoInstructionandSetAllJobTriggersErrorstill tests the original exception, so cancellation and disposal races behave exactly as before.TimeZoneInfo.GetUtcOffsetreturning the pre-gap offset for positive-delta zones. It is now two internalTimeZoneUtilhelpers —ResolveLocal(ambiguous → the first/daylight occurrence; nonexistent → paired with the pre-gap offset, found by scanning backwards a minute at a time so 30-minute deltas and negative-delta zone models work) andWalkToGapEnd— with all five call-site families migrated onto them. Two defects fell out: a negative-daylight-delta gap hazard, where a zone modelled with a negative delta (Europe/Dublin on TZif data, so Linux and macOS) produced an instant before the gap — a scheduler hot-loop hazard the ambiguity demotion does not cover; and a sub-second demotion defect, whereCronExpression.GetTimeAftercompared its whole-second candidate against the untruncated after-time, so a sub-second after-time inside the repeated fall-back hour demoted a valid fire a whole hour forward. The migrations are behaviour-neutral for every positive-delta zone, guarded by a minute-by-minute differential test across ±3 hours of all eight test zones' transitions. (#3198, building on #3195)GetTimeAftermonotonic in its argument again, and stopsGetTimeBeforereturning instants the expression never fires at. Fire times inside the repeated fall-back hour can therefore differ from 3.19.DailyTimeIntervalTriggerno longer fires past itsEndTimeUtc— the day-of-week advance resolved the advanced day's start-of-day through the instant-based offset overload while every sibling call site used the wall-clock policy overload, so the offset carried through theAddDayswalk was stale once the walk crossed a transition, and an in-gap start-of-day resolved one transition delta too early. TheEndTimeUtccheck inside the same method consumed the mis-resolved instant before the downstream rescue corrected it, so with an end time between the wrong instant and the right one the trigger fired once past its configured end. Only triggers with a partialDaysOfWeekset were affected — the existing tests all use a full week, where the two resolutions coincide. (#3196)HolidayCalendar,AnnualCalendar,MonthlyCalendarandWeeklyCalendarbuilt the boundary they walk as midnight at the offset the queried instant happened to carry, and advanced it withAddDays, which keeps that offset. Both assume a local day begins at midnight at the same offset the rest of the day carries, and a transition day is exactly the day where it does not. In America/Santiago, where the clocks move at midnight, the 23-hour day (2019-09-08) answered with the excluded instant it was asked about, and the 25-hour day (2019-04-06) overshot by 23 hours; in a zone that moves its clocks later in the day the answer lands a whole date out (Europe/Helsinki on 2024-03-31, asked from the afternoon, answered 2024-03-30); and a run of excluded days crossing a transition drifts by the transition delta. The boundary now resolves local midnight by naming the date rather than by adding twenty-four hours.CronCalendarnever had the shape. (#3478, fixes #3457)DailyCalendar.GetNextIncludedTimeUtcis jumped to rather than walked up to — it named the window's edges on the date the argument fell on and paired them with the argument's offset, whileIsTimeIncludedconverts into the calendar's zone first, so a question asked in UTC about a calendar keeping another zone's hours had the two disagree. The jump then landed on a window an offset away from the one being tested, the loop fell through to its last resort — one precision step at a time, bounded only by where the wrong window happened to end — and it could step clean over an included stretch. The measured reproduction, an inverted 21:00–22:00 America/Santiago calendar asked at2019-04-07T01:30Z:2019-09-09T00:00:59.999Z, five months late, reached a minute of wall clock at a time, against2019-04-08T01:00Zin two passes now. The whole daily-calendar half of the new test fixture takes 1 m 52 s against the unfixed file and milliseconds against this one. The answer is now named from the window's own edges — the instant the day opens at, the second reading of that edge where the clock repeats it, the instant the clocks moved where a fall-back takes the clock back to before the window opened, and the day's own first instant — each checked against the calendar's own rule before it is taken. (#3478, fixes #3466)precisionStepMillisoptimisation (#2285) is removed, because a jump has nothing left to speed up and it was rounding the answer up by as much as a minute.GetNextIncludedTimeUtcanswers are now exact: a06:00–22:00calendar asked at21:59Zreturns22:00:00.001Zwhere it returned22:01:00.000Z, and asking about an instant the calendar already includes gives back the next millisecond rather than the next minute. TheNestedCalendarTestsperformance test that came with #2285 is unchanged and passes.GetTimeRangeStartingTimeUtcandGetTimeRangeEndingTimeUtcare public and carried the same offset assumption in public — asked in UTC about a Santiago calendar they answered about the UTC date at+00:00, which no caller can use. They now read the date of the local day the instant falls in. For a calendar left on the defaultTimeZoneInfo.Localasked with a local value, the conversion is a no-op. No signature changed.UseNewtonsoftJsonSerializerleavesRegisterTriggerConvertersoff by default, and with it off Json.NET's default contract wrote aTimeZoneInfoas its whole public surface (Id,DisplayName,BaseUtcOffset, all read-only), so reading it back set nothing and the trigger's getter fell through toTimeZoneInfo.Local. A trigger stored under Tokyo fired on whichever zone the reading machine was in, silently. Measured before the fix:CalendarIntervalTriggerImpl,RecurrenceTriggerImplandCronTriggerImplall came back on the reading machine's zone. An internalTimeZoneInfoConverterwrites the id and reads both the id and the old object form, attached per property to members typed as aTimeZoneInfo— deliberately not on the serializer's converter list, since that list is consulted for a value's runtime type wherever it appears and a zone held in a job data map value would then lose the$typethat path carries. The four privatetimeZoneInfoIdhelpers that were meant for this and never worked (DefaultContractResolverdoes not serialize private members) are gone.BLOB_TRIGGERSpayloads written byBinaryObjectSerializerare unaffected: they are computed properties, so there was never a backing field forBinaryFormatterto match. (#3505)DailyTimeIntervalTriggerImplcannot be read back at all —TimeOfDayhas no parameterless constructor, so Json.NET fails loudly onEndTimeOfDay. It predates this change, it fails rather than corrupting, and the ADO store reaches that path only for a trigger it serializes as a blob, so a shippedDailyTimeIntervalTriggerdoes not go through it.JobInterruptMonitorPlugininterrupted by job key, so a monitor that elapsed cancelled every running execution of that job rather than the one it was watching. Worse, a vetoed fire's monitor was never cancelled at all, because the only cleanup point wasTriggerComplete, which a vetoed fire never reaches — so every veto leaked a live monitor that would later interrupt an unrelated, healthy execution of the same job. It now callsInterrupt(fireInstanceId), cancels the monitor on veto through a job listener of its own, does not start a second monitor for a re-executed job sharing a fire instance id, and removes its own bookkeeping entry when it elapses. Both scenarios are exactly as analysed in the report. (#3249, fixes #3248)IScheduler.Interrupt(fireInstanceId)now raisesISchedulerListener.JobInterrupted, matching theInterrupt(JobKey)overload — relevant to anyone calling the fire-instance overload directly. AndAutoInterruptableis now read from the merged job data map, consistent withMaxRunTime, so a trigger's data map can opt a fire in or out of auto-interruption rather than only override the timeout.CALENDAR_NAMEwas written as''rather thanNULLstopped firing entirely: every job store gates its calendar lookup onCalendarName is not null, so the empty string passed the gate, the lookup found nothing, and the fire was silently dropped.AbstractTrigger.CalendarNamenow stores a blank name asnull, without trimming — the name is a lookup key against whateverAddCalendarstored, so trimming" holidays "would break a calendar registered with padding and create the same bug from the other direction. That one setter is the choke point forTriggerBuilder, both JSON converters and the ADO read-back, so databases already holding''self-heal: the row rehydrates as "no calendar", the trigger fires again on the next acquisition, and the column is written back asNULLnext time it is persisted. No migration script.TriggerDetailsUpdate.WithCalendarNamenormalizes separately, since both stores check the calendar exists before the value reaches the trigger setter. Both stores now also log when they skip a fire over a missing calendar, which turns this class of report from a mystery into a one-line diagnosis. The dashboard, which produced the empty string by rebuilding the trigger field by field out of its display projection, now edits the trigger JSON it was already handed — which also preserves the node pin and the real trigger type it used to drop. (#3295, fixes #3294)CronCalendar.GetNextIncludedTimeUtcnever terminated from an excluded instant (it advanced withGetNextValidTimeAfter, which lands on another excluded time);CronCalendar's three-argument constructor dropped itstimeZone, so the calendar evaluated its exclusion cron in machine-local time; transient-error classification short-circuited onDbException.IsTransient, which on any .NET 6+ host matches every driver exception — so the deadlock-1205 list, the SQLite busy/locked check and the timeout fallback were all dead code and retryable failures were classified permanent; a prefix trigger-group pause paused only the first matching group and a prefix resume could never clear what a prefix pause recorded;JobInterruptMonitorPluginsilently ignored a numericMaxRunTime, falling back to the five-minute default with no log; and Newtonsoft deserialization populated read-only collections through their getters, so on the default configuration aDaysOfWeeksubset came back as all seven days. (#3334)DaysOfWeeksubset survives the plain Newtonsoft round trip. Deliberately not backported: the 4.x store-parity semantic alignments (pause-over-Error,ResumeAllmarker clearing, sentinel visibility, exception re-wrap types), which change observable maintenance-branch behaviour.SelectInstancesFiredTriggerRecordswas the only fired-trigger reader that never readPRIORITYandClusterRecoverassigns from exactly that reader, so every recovery trigger ran at priority 0, below the default 5, deprioritising recovery work precisely when a node has died. FourIDriverDelegatemembers failed on every provider because their bound parameter names never matched their SQL; nothing in the scheduler calls them, which is why it never surfaced, but they are public surface reachable fromJobStoreSupportsubclasses. And group matcher values are now escaped, with!as the escape character becauseESCAPE '\'is a MySQL syntax error while!is a plain literal on all six supported databases. (#3202)%or_now match literally in group matcher queries. Previously those characters acted as LIKE wildcards, so a matcher could list, pause, resume or delete groups it was not meant to match. The same correction meansResumeAlldeletes only the_$_ALL_GROUPS_PAUSED_$_sentinel row rather than a pattern in which all eight underscores were single-character wildcards.sched_name, which every Quartz statement filters on first, andidx_qrtz_t_nft_sthad its columns reversed —(next_fire_time, trigger_state)against an acquire query that is two equalities then a range, so the index could never bound the scan on state or scheduler name. This is the shape behind slow-acquire-on-PostgreSQL reports. Verified on live PostgreSQL 16: the upgrade script and a fresh install converge on byte-identicalpg_indexessets, and a 160,000-trigger acquire-plan comparison moves all three predicates from post-filter into the index condition — buffer hits 136 → 57, discarded rows 61 → 0. Across the other dialects, indexes whose columns are a leftmost prefix of a wider same-table index are dropped, with the coverer named on every drop.IDX_QRTZ_J_GRPdeliberately stays on 3.x, unlike 4.x, because its 4.x coverer does not exist here and it serves the group listings. The migration is optional and performance-only —database/migrations/3.20/index_alignment_<dialect>.sql. (#3203)database/migrations/<version>/<name>_<dialect>.sql, one directly-runnable file per database instead of one file carrying five commented-out dialect blocks, with every statement guarded so re-running is a no-op (SQLiteADD COLUMNis the documented exception — SQLite has no conditional DDL). The scripts are generated from one description in the build, because six hand-written dialect variants is how they drift, andVerifyMigrationsfails a checked-in script that no longer matches.database/README.mdis the new index: run order, per-version status, and the old-path → new-path map reproduced below. The migrations previously had no test coverage at all;MigrationScriptTestnow builds a 3.16-era schema from a checked-in baseline, applies 3.17 → 3.18 → 3.19 → 3.20 in order, applies each twice so the guards are exercised, and asserts the result matches what the currenttables_<dialect>.sqlproduces, table for table, column for column, index for index — on all six databases. It caught two real defects on the way: PostgreSQL's index alignment usedCREATE INDEX IF NOT EXISTSfor three indexes that already exist in a 3.16 schema under the same name with different columns, so the guard silently kept the wrong shape — includingidx_qrtz_t_nft_st; and MySQL'sQRTZ_BLOB_TRIGGERScarried an InnoDB-auto-named inline index duplicating its primary key that the migration had left in place. It also backports the 2.5→2.6QRTZ_CRON_TRIGGERS.TIME_ZONE_IDfix (#1985), which never reached this branch. (#3219, fixes #3218)main, and this branch links to them — they were briefly mirrored here, and a mirror goes stale silently every time 4.x's schema moves. A confidently wrong upgrade script is worse than one that is plainly somewhere else, sodatabase/migrations/4.0/is not on this branch anddatabase/README.mdpoints at https://github.com/quartznet/quartznet/tree/main/database/migrations/4.0 in every place it used to link to the folder. (#3373, and #3326, which stopped the SQLite 4.0 upgrade claiming it can be re-run when its fiveADD COLUMNs are unguarded)git diff(#3226). TheUnitTesttarget now asks each project which frameworks it declares and runs it once per framework:dotnet test --framework Xagainst a project that does not target X exits 0 having run nothing, so the Ubuntu and macOS legs had been running 116 tests and silently skipping the 1,753 inQuartz.Tests.Unitwhile the green check said otherwise (#3228). CI was unbroken by moving to Testcontainers 4.14.0, which references a patchedSSH.NET, and by pinningNuGet.Frameworksforward for SDK 10.0.400 (#3278). The build orchestrator moved to Fallout 10.4.0 (#3252),changelog.mdwas retired in favour of the GitHub releases that had already superseded it (#3224),sonar-project.propertieswas removed because SonarCloud's automatic analysis never read it (#3221), and a test that had been writing files into the application directory on every CI run stopped (#3293).Public API
Additive only. No existing signature changed, nothing was removed, and the new baselines are byte-identical across every other change in this release.
Quartz.Core.JobInstantiationException : SchedulerException, carryingTrigger,JobDetailandFireInstanceId(#3215)IScheduler.EnlistTransaction(DbTransaction)andIScheduler.EnlistConnection(DbConnection), plusAcceptEnlistedTransactions()on the persistent store builder and thequartz.jobStore.acceptEnlistedTransactionskey (#3204)Where the database scripts moved
The scripts used to sit flat in
database/, with the dialects other than SQL Server commented out inside each file. Those paths are gone from the branch tip. Old links keep working against release tags — for examplehttps://github.com/quartznet/quartznet/blob/v3.19.1/database/schema_30_add_preferred_node.sql.database/sqlserver_schema_10_to_20_upgrade.sqldatabase/schema_10_to_20_upgrade.sqlmigrations/2.0/schema_10_to_20_upgrade_sqlServer.sqldatabase/schema_20_to_22_upgrade.sqlmigrations/2.2/schema_20_to_22_upgrade_<db>.sqldatabase/schema_25_to_26_upgrade.sqlmigrations/2.6/schema_25_to_26_upgrade_<db>.sql3.19.1
Quartz.NET 3.19.1 is a small bug fix release with two targeted fixes:
DailyTimeIntervalTriggerno longer gets stuck in an infinite fire loop on DST spring-forward days, andStdSchedulerFactory.GetScheduler(schedName)now creates the scheduler when the name asked for is its own. There are no API or schema changes, so it is a drop-in upgrade from 3.19.0.Highlights
DailyTimeIntervalTriggerno longer spins on DST transition days —GetFireTimeAftercould return a time at or before the one it was given, which makesQuartzSchedulerThreadfire the trigger, compute the same next fire time, and fire again — pinning a CPU core and flooding the log. Two independent causes, both on a spring-forward day: the DST correction added for #1114 was applied to every interval size and in either direction (so every interval of an hour or less was affected, in every DST time zone), and the daily rollover toStartTimeOfDayreused whatever UTC offset the previous fire time carried (so in time zones that move the clock at midnight, such as Chile,StartTimeOfDay00:00 resolved to an instant before the transition — the same instant that was passed in). Verified across 3024 combinations of 12 time zones, both transitions, 21 intervals and 6 start times: 468 combinations produced non-advancing fire times before, none do now. (#3190, fixes #332)StdSchedulerFactory.GetScheduler(schedName)creates its own scheduler — asking a factory for the scheduler it is configured to produce returnednulluntil somebody had calledGetScheduler()first. It now creates it. Any other name stays a pure lookup, so probing for a scheduler somebody else owns still has no side effects, and the name comparison is case-insensitive to match howSchedulerRepositoryindexes names. The DI factory has behaved this way since #2845; this brings the property-configured factory in line. (#3188, reported in #2786, originally proposed in #360)What's Changed
Full Changelog: quartznet/quartznet@v3.19.0...v3.19.1
3.19.0
Quartz.NET 3.19.0 is a feature release: it adds node affinity for clustered scheduling, a fluent cron-expression builder, and richer
L/LWday-of-month expressions, plus clock-jump resilience and a modernized build and publishing pipeline. The public API is unchanged (all additions are additive), so it is a drop-in upgrade — with two things to note: the new node-affinity columns are an optional schema migration (the feature degrades gracefully without them), and a handful of previously-brokenL/LW/Wcron expressions now fire correctly (see below).Highlights
TriggerBuilder.WithPreferredNode(...); the node is preferred for acquisition but the trigger is still stolen on failover so it is never stranded if that node goes down. Adds optionalPREFERRED_NODE/PREFERRED_NODE_AUTOcolumns for ADO.NET job stores (database/schema_30_add_preferred_node.sql); when the columns are absent the scheduler logs a warning and behaves exactly as before. (#3013, #3144)CronExpressionBuilder— compose cron expressions programmatically, one field at a time, instead of hand-writing the string — handy when a schedule is assembled from user input such as a scheduling UI. (#3139)LandLWcombinable with other day-of-month values — the day-of-month field now accepts expressions such as1,15,Land the newLW-n/L-nWgrammar. This also corrects several previously-buggy edge cases:29W/31Wno longer silently skip short months,L-30Wno longer throws mid-schedule, and1,15Wnow appliesWto each day rather than only the first. These corrections change the fire times of a few expressions that were previously broken — review any storedL/LW/W...Description has been truncated