Skip to content

Releases: quartznet/quartznet

v3.18.2

Choose a tag to compare

@lahma lahma released this 27 Jun 06:36

Quartz.NET 3.18.2 is a maintenance release that stabilizes the dashboard introduced in 3.18.0, fixes several scheduling correctness bugs, and speeds up cron next-fire-time computation. It is a drop-in upgrade from earlier 3.18.x releases — no breaking changes and no database schema migrations.

Highlights

  • Type-load failures no longer stall the whole scheduler — a trigger or job whose type failed to load could throw inside AcquireNextTrigger and block acquisition of every other trigger. The faulting trigger is now isolated so the rest keep firing. (#3108)
  • "Reschedule next" misfire policies no longer fire immediately — after a misfire, the reschedule-next policies could fire once right away instead of waiting for the next scheduled time. (#3100)
  • Named-scheduler JSON config is honored — schedules defined under the root Quartz configuration section were not loaded for named schedulers. (#3113)
  • Dashboard fixes — authorization policy no longer leaks to host-app endpoints (#3067); static assets and a custom DashboardPath work under a fail-closed FallbackPolicy, and the Blazor circuit is allowed anonymous access under the same policy (#3098, #3120); trigger and calendar JSON deserialization is fixed (#3102); and JobDataMap and SimpleSchedule trigger details now display correctly (#3132).
  • MySQL FORCE INDEX fix — schema-qualified table prefixes produced a malformed FORCE INDEX hint. (#3086)
  • Faster cron scheduling — cron next-fire-time computation gains a bitmask fast path and avoids needless DateTimeOffset churn in the hot loop. (#3126, #3129)
  • Smaller enhancements — health-check tags can now be passed to AddQuartzServer (#3112), and new public helpers make plugin configuration extensible (#3104).

What's Changed

  • Fix dashboard authorization policy leaking to host app endpoints (#3066) by @lahma in #3067
  • Fix malformed MySQL FORCE INDEX hint with schema-qualified table prefix by @lahma in #3086
  • Fix dashboard FallbackPolicy asset blocking, honor custom DashboardPath, document Blazor integration (3.x) by @lahma in #3098
  • Fix reschedule-next misfire policies firing immediately after misfire (#3096) by @lahma in #3100
  • Fix dashboard trigger and calendar JSON deserialization (#3094) by @lahma in #3102
  • Fix type-load exceptions in AcquireNextTrigger blocking all other triggers by @lahma with @Copilot in #3108
  • Fix JSON schedule config not loaded for named schedulers using root Quartz section (#3106) by @lahma in #3113
  • Allow passing health check tags to AddQuartzServer by @lahma in #3112
  • Strip 3.x branch docs to NuGet package READMEs only (#3116) by @lahma in #3119
  • Allow the dashboard Blazor circuit anonymous access under a fail-closed FallbackPolicy (#3117) by @lahma in #3120
  • Add public plugin configuration extensibility helpers (3.x) by @lahma in #3104
  • Speed up cron next-fire-time computation with a bitmask fast path by @lahma in #3126
  • Reduce DateTimeOffset churn in the cron next-fire-time loop (3.x) by @lahma in #3129
  • Fix dashboard JobDataMap and SimpleSchedule trigger display (3.x) by @lahma in #3132

Full Changelog: v3.18.1...v3.18.2

v3.18.1

Choose a tag to compare

@lahma lahma released this 25 Apr 11:32

Quartz.NET 3.18.1 is a maintenance release that addresses a timezone-related bug, security advisories on transitive dependencies, and an issue that prevented the new Redis distributed-lock package from publishing to nuget.org.

Highlights

  • Fix GetTimeBefore for positive-offset timezonesCronExpression.GetTimeBefore could throw ArgumentOutOfRangeException for cron expressions evaluated against timezones with positive UTC offsets (e.g. Europe/Helsinki, Asia/Tokyo). Backported from main.
  • Redis package renamed to Quartz.Extensions.Redis — the original Quartz.Redis package id from 3.18.0 collided with an unrelated v1.0.0 already on nuget.org and could not be published. The 3.18.0 release was shipped with IsPackable=false as a workaround; 3.18.1 picks the umbrella id Quartz.Extensions.Redis (matching Quartz.Extensions.DependencyInjection / Quartz.Extensions.Hosting) and re-enables publication. There are no consumers to migrate — the original id was never on nuget.org.
  • Vulnerable transitive dependency bumps — addresses moderate advisories that were blocking restore on the 3.x CI pipeline.

What's Changed

  • Fix GetTimeBefore throwing for positive-offset timezones (#3046) by @lahma in #3047
  • Bump vulnerable packages to unblock 3.x CI by @lahma in #3054
  • Rename Quartz.Redis package to Quartz.Extensions.Redis by @lahma in #3053

Full Changelog: v3.18.0...v3.18.1

v3.18.0

Choose a tag to compare

@lahma lahma released this 11 Apr 09:18

Quartz.NET 3.18.0 is a major feature release with 8 new capabilities, performance improvements, and important bug fixes.

New Features

RFC 5545 RRULE recurrence trigger

Quartz.NET now supports scheduling with iCalendar recurrence rules (RFC 5545 RRULE), enabling complex patterns that cannot be expressed with cron expressions — such as "2nd Monday of every month" or "last weekday of March each year."

A custom lightweight RRULE engine (~1.5K LOC) handles all frequencies (YEARLY through SECONDLY), all BY* rules (BYDAY, BYMONTHDAY, BYSETPOS, etc.), COUNT, UNTIL, INTERVAL, and WKST. No new external dependencies. No database schema changes — uses the existing SIMPROP_TRIGGERS table.

ITrigger trigger = TriggerBuilder.Create()
    .WithIdentity("myTrigger")
    .WithRecurrenceSchedule("FREQ=MONTHLY;BYDAY=2MO", b => b
        .InTimeZone(TimeZoneInfo.FindSystemTimeZoneById("America/New_York")))
    .StartNow()
    .Build();
RRULE Pattern
FREQ=MONTHLY;BYDAY=2MO Every 2nd Monday of the month
FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,WE,FR Every other week on Mon/Wed/Fri
FREQ=YEARLY;BYMONTH=3;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1 Last weekday of March each year
FREQ=MONTHLY;BYMONTHDAY=-1 Last day of every month
FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR Every weekday

(#2990) — Closes #1259

Execution groups for per-node thread limits

Tag triggers with an execution group to limit how many threads a category of jobs can consume concurrently on each node. This prevents resource-intensive jobs from starving lightweight work.

services.AddQuartz(q =>
{
    q.UseExecutionLimits(limits =>
    {
        limits.ForGroup("batch-jobs", maxConcurrent: 2);
        limits.ForDefaultGroup(maxConcurrent: 10);
        limits.ForOtherGroups(maxConcurrent: 5);
    });

    q.AddTrigger(t => t
        .ForJob("heavyJob")
        .WithExecutionGroup("batch-jobs")
        .WithCronSchedule("0 0/5 * * * ?"));
});

Also configurable via properties (quartz.executionLimit.batch-jobs = 2) and runtime API (scheduler.SetExecutionLimits(...)). Includes optional EXECUTION_GROUP column for ADO.NET job stores with graceful fallback when absent, and Dashboard integration.

(#3004) — Closes #1175, #830

Multiple named schedulers in Microsoft DI

Register multiple independent scheduler instances in a single DI container. Each named scheduler gets isolated options, jobs, triggers, listeners, and calendars.

services.AddQuartz("Scheduler1", q =>
{
    q.AddJob<EmailJob>(j => j.WithIdentity("email"));
    q.AddTrigger(t => t.ForJob("email").WithCronSchedule("0 0/5 * * * ?"));
});

services.AddQuartz("Scheduler2", q =>
{
    q.UsePersistentStore(s => { /* ... */ });
    q.AddJob<ReportJob>(j => j.WithIdentity("report"));
    q.AddTrigger(t => t.ForJob("report").WithCronSchedule("0 0 * * * ?"));
});

services.AddQuartzHostedService(o => o.WaitForJobsToComplete = true);

AddQuartzHostedService() automatically manages the lifecycle of all registered schedulers.

(#3000) — Closes #2109

JSON configuration and scheduling data

Configure Quartz.NET using hierarchical JSON in appsettings.json instead of flat property keys. Supports declarative job and trigger definitions, all 4 trigger types, and named schedulers via a Schedulers section.

{
  "Quartz": {
    "Scheduler": { "InstanceName": "My Scheduler" },
    "ThreadPool": { "MaxConcurrency": 10 },
    "Schedule": {
      "Jobs": [{ "Name": "myJob", "JobType": "MyApp.Jobs.MyJob, MyApp", "Durable": true }],
      "Triggers": [{ "Name": "myTrigger", "JobName": "myJob", "Cron": { "Expression": "0/30 * * * * ?" } }]
    }
  }
}
services.AddQuartz(Configuration.GetSection("Quartz"));

Also includes a standalone JsonSchedulingDataProcessorPlugin for quartz_jobs.json file support with hot-reload, mirroring XMLSchedulingDataProcessorPlugin.

(#3012, #3015, #3017) — Closes #1755

Redis-based distributed lock handler (new Quartz.Extensions.Redis package)

Note: This package is published as Quartz.Extensions.Redis starting with 3.18.1. The package was authored as part of 3.18.0 but had to ship with IsPackable=false because the originally-chosen id Quartz.Redis was already taken on nuget.org by an unrelated v1.0.0. See #3053. To use this feature, install Quartz.Extensions.Redis 3.18.1 or later.

New Quartz.Extensions.Redis NuGet package providing RedisSemaphore — an ISemaphore implementation using Redis SET NX PX distributed locks instead of database row locks. This eliminates DB row lock contention and deadlocks in clustered setups while keeping job/trigger data in the relational database.

Uses two-tier locking: local SemaphoreSlim prevents redundant Redis round-trips within the same process, and a Lua script ensures atomic check-and-delete on release for safety.

services.AddQuartz(q =>
{
    q.UsePersistentStore(store =>
    {
        store.UseRedisLockHandler(redis =>
        {
            redis.RedisConfiguration = "redis-server:6379";
        });
    });
});

(#2999, #3053) — Closes #1625

Activity tracing for ADO.NET job store operations

28 IJobStore methods are now wrapped with System.Diagnostics.Activity spans, so database calls appear as children of named Quartz operations (e.g., Quartz.JobStore.AcquireNextTriggers) instead of orphaned root spans in your tracing system. Zero overhead when tracing is disabled.

[Quartz.JobStore.AcquireNextTriggers]
  └── [SELECT ... FROM TRIGGERS]
  └── [INSERT ... INTO FIRED_TRIGGERS]
[Quartz.JobStore.TriggersFired]
  └── [UPDATE ... TRIGGERS SET STATE=...]
[Quartz.Job.Execute]
  └── [user job work]

All new operations are automatically included in QuartzInstrumentationOptions.DefaultTracedOperations.

(#3001) — Closes #2721

UpdateTriggerDetails — update trigger metadata without rescheduling

New UpdateTriggerDetails method updates Description, Priority, JobDataMap, CalendarName, and MisfireInstruction on an existing trigger without resetting fire times, trigger state, or misfire context. Available via IScheduler.UpdateTriggerDetails() extension method.

(#2988) — Closes #844

Factory-based AddQuartz() with IServiceProvider access

New AddQuartz overloads accepting Action<IServiceCollectionQuartzConfigurator, IServiceProvider> allow resolving DI services during Quartz configuration — useful for obtaining connection strings, feature flags, or other configuration from DI-registered services.

services.AddQuartz((q, sp) =>
{
    var config = sp.GetRequiredService<DatabaseConfig>();
    q.UsePersistentStore(s =>
    {
        s.UseSqlServer(sql => sql.ConnectionString = config.ConnectionString);
    });
});

(#3007) — Closes #1617

Performance

Reduced DB round-trips in misfire recovery

Misfire recovery now uses a targeted UPDATE instead of routing each trigger through the full StoreTrigger path, reducing per-trigger DB round-trips from 7-12 down to 1-2 (~87% reduction). For a batch of 20 cron triggers, this drops from ~150 queries to ~20 queries — all under LockTriggerAccess. Calendar lookups are also cached across the batch.

(#2993) — Closes #758

Bug Fixes

  • Fix scheduler signal loss causing triggers stuck in WAITING stateSemaphoreSlim(0, 1) in QuartzSchedulerThread could silently drop scheduling signals via SemaphoreFullException, causing triggers to stay stuck in WAITING state until the next idle loop timeout. (#3033) — Fixes #3028
  • Fix SchedulerRepository preventing connections to multiple cluster nodesSchedulerRepository indexed by scheduler name only, preventing multiple remote proxies to different nodes in the same cluster from coexisting. Now supports instance-aware lookup. (#2991) — Fixes #388
  • Implement IsJobGroupPaused/IsTriggerGroupPaused in ADO.NET job store — These methods previously threw NotImplementedException in the persistent job store. (#3030)
  • Fix Dashboard Live not working over HTTP — Dashboard live updates now work correctly over plain HTTP connections. (#3032)
  • Fix dashboard plugins using static ServiceProvider — Dashboard plugins no longer rely on a static ServiceProvider reference, fixing issues with multiple host instances. (#3035) — Fixes #3026

Deprecations

  • DirtyFlagMap.Get() — use the indexer (map[key]) instead (#2986)
  • DirtyFlagMap.Put() / PutAll() — use the indexer or collection initializer instead (#2989)

What's Changed

  • Improve DirtyFlagMap dirty flag correctness and mark Get() obsolete by @lahma in #2986
  • Mark Put/PutAll as [Obsolete] and migrate internal callers to indexer by @lahma in #2989
  • Fix SchedulerRepository preventing connections to multiple cluster nodes by @lahma in ht...
Read more

v3.17.1

Choose a tag to compare

@lahma lahma released this 03 Apr 08:09
ea14a9a

Highlights

Jenkins-style H (hash) token for cron expressions

Quartz.NET now supports the H (hash) token in cron expressions, inspired by Jenkins. The H token resolves to a deterministic value based on the trigger's identity, spreading job execution times to avoid the thundering herd problem when many triggers share the same schedule.

Supported forms: H, H(min-max), H/step, H(min-max)/step

// Runs at a consistent but spread-out minute each hour
trigger = TriggerBuilder.Create()
    .WithIdentity("myTrigger", "myGroup")
    .WithCronSchedule("0 H * * * ?")
    .Build();

// Runs every 15 minutes, starting at a hash-determined offset
trigger = TriggerBuilder.Create()
    .WithIdentity("myTrigger", "myGroup")
    .WithCronSchedule("0 H/15 * * * ?")
    .Build();

The hash seed is automatically derived from the trigger's identity (name + group) when using TriggerBuilder, or can be provided explicitly. See the cron trigger documentation for full details.

Structured logging history plugins

New StructuredLoggingJobHistoryPlugin and StructuredLoggingTriggerHistoryPlugin provide first-class support for structured logging frameworks like Serilog and NLog. Unlike the existing logging plugins that use index-based format placeholders ({0}, {1}), these use named MEL-style message template parameters ({JobName}, {TriggerGroup}, etc.), making log output queryable in structured logging sinks and avoiding template cache memory leaks.

quartz.plugin.jobHistory.type = Quartz.Plugin.History.StructuredLoggingJobHistoryPlugin, Quartz.Plugins
quartz.plugin.triggerHistory.type = Quartz.Plugin.History.StructuredLoggingTriggerHistoryPlugin, Quartz.Plugins

Message templates are fully configurable via standard Quartz property configuration.

Bug Fixes

  • Fix triggers getting permanently stuck in ACQUIRED state — Triggers could become permanently stuck if the scheduler crashed or was killed between acquiring a trigger and firing it. (#2980)
  • Fix StoreCalendar overriding paused trigger state — Storing a calendar with updateTriggers: true no longer resets paused triggers back to normal state. (#2968)
  • Fix DoCheckin not retrying transient database errors — Cluster checkin now properly retries on transient database failures instead of silently failing. (#2970)
  • Fix Dashboard /_blazor endpoint conflict — The Quartz Dashboard no longer conflicts with host applications that also use Blazor. (#2975)
  • Fix spurious ERROR log on zombie transaction rollback — Rolling back a zombie transaction no longer logs a misleading ERROR. (#2978)

Full Changelog: v3.17.0...v3.17.1

v3.17.0

Choose a tag to compare

@lahma lahma released this 29 Mar 19:07

This is a major bug-fix release with 40+ fixes spanning trigger state management, clustering reliability, DST handling, misfire accuracy, and scheduler lifecycle. An optional database schema migration improves misfire handling.

Highlights

Optional database migration: MISFIRE_ORIG_FIRE_TIME column

A new optional column MISFIRE_ORIG_FIRE_TIME on QRTZ_TRIGGERS enables correct ScheduledFireTimeUtc for misfired triggers when using "fire now" misfire policies. Without it, ScheduledFireTimeUtc equals FireTimeUtc for misfired triggers (the pre-existing behavior). RAMJobStore does not require this migration.

Migration script: database/schema_30_add_misfire_orig_fire_time.sql (covers SQL Server, PostgreSQL, MySQL, SQLite, Oracle, Firebird)

Clustering and concurrency fixes

  • Fix DisallowConcurrentExecution jobs running simultaneously in cluster (#2697)
  • Fix false cluster recovery causing DisallowConcurrentExecution violation (#2915)
  • Fix triggers stuck in BLOCKED state for DisallowConcurrentExecution jobs (#2822)
  • Fix FIRED_TRIGGERS not cleaned up on job deletion mid-execution (#1696)
  • Handle transient database exceptions (deadlocks) with automatic retry (#2883, #2952)
  • Fix PostgreSQL transaction abort error on lock contention (#2884)
  • Fix SQLite "database is locked" errors with dedicated semaphore (#2323)
  • Add MySQL FORCE INDEX hints for slow trigger acquisition queries (#547)

Trigger state and fire time accuracy

  • Fix GetTriggerState returning Complete instead of Blocked for executing triggers (#2255)
  • Fix RAMJobStore.TriggersFired skipping triggers causing wrong trigger/job execution (#1386)
  • Fix ScheduledFireTimeUtc returning wrong value after misfire (#2899)
  • Fix PreviousFireTimeUtc reset to null on restart with OverWriteExistingData=true (#1834)
  • Fix SimpleTrigger first fire time wrong when created long before scheduling (#2455)
  • Fix CronTrigger double-firing when rescheduled with old StartTimeUtc (#2909)
  • Fix recovery trigger missing original JobData (#2083)
  • Fix misfired blocked triggers not being deleted from database (#1646)
  • Fix DoNothing misfire policy skipping fire times within threshold (#2912)

DST and time handling

  • Fix DailyTimeIntervalTrigger extra fire during DST fall-back (#2917)
  • Fix DailyTimeIntervalTrigger mutating StartTimeUtc during fire time computation (#2906)
  • Fix DailyTimeIntervalTrigger RepeatCount to apply per day (#1633)
  • Fix scheduler thread stuck after system clock jumps backward (#1508)
  • Fix ComputeFireTimesBetween modifying trigger StartTimeUtc (#2722)

Scheduler lifecycle and threading

  • Fix scheduler hang on shutdown: replace Monitor.Wait with async SemaphoreSlim (#2877)
  • Fix shutdown deadlock (#2830)
  • Release acquired triggers on shutdown instead of leaking them (#2881)
  • Fix DedicatedThreadPool threads leaking on scheduler shutdown (#1357)
  • Use dedicated thread for scheduler loop to prevent missed triggers under high CPU (#781)

Job execution

  • Fix RefireImmediately with JobChainingJobListener firing chain prematurely (#663)
  • Fix AsyncLocal flow from IJobFactory.NewJob to IJob.Execute (#1528)
  • Add IJobDetail property to JobExecutionException (#1442)

API and configuration

  • Fix idleWaitTime of zero silently ignored instead of throwing (#1394)
  • Fix AddJob/AddTrigger ambiguous references without removing overloads (#2795)
  • Fix PauseJobs/ResumeJob interaction bug in RAMJobStore (#761)
  • Fix RemoteScheduler ignoring local quartz.scheduler.instanceName (#313)
  • Restore DB trigger fields for custom (blob) triggers (#2949)

Dashboard

  • Fix dashboard CSS 404 in API-only projects (#2886)

What's Changed

  • Add IJobDetail property to JobExecutionException by @lahma in #2869
  • Fix ComputeFireTimesBetween modifying trigger StartTimeUtc by @lahma in #2870
  • Convert integration tests to use Testcontainers.NET by @lahma in #2873
  • docs: document RequiresAspNetWebAssets for API-only apps (.NET 10+) by @lahma in #2875
  • Fix DoCheckin lock returner warning and ReferenceEquals comparisons by @lahma in #2876
  • Fix scheduler hang on shutdown: replace Monitor.Wait with async SemaphoreSlim by @lahma in #2877
  • Release acquired triggers on shutdown instead of leaking them by @lahma in #2881
  • Port: Handle transient database exceptions with automatic retry (3.x) by @lahma in #2883
  • Port PostgreSQL transaction abort fix to 3.x (#2829) by @lahma in #2884
  • Port DST fall-back infinite trigger fix to 3.x (#2865) by @lahma in #2885
  • Fix dashboard CSS 404 in API-only projects (#2886) by @lahma in #2887
  • Port shutdown deadlock fix to 3.x (#2830) by @lahma in #2889
  • Use dedicated thread for scheduler loop to prevent missed triggers under high CPU (#781) by @lahma in #2895
  • Fix triggers stuck in BLOCKED state for DisallowConcurrentExecution jobs by @Copilot in #2822
  • Split integration tests into per-database CI workflows by @lahma in #2900
  • Fix ScheduledFireTimeUtc returning wrong value after misfire by @lahma in #2899
  • Remove docker folder and docker-compose.yml by @lahma in #2904
  • Fix recovery trigger missing original JobData (#2083) by @lahma in #2905
  • Fix CronTrigger double-firing when rescheduled with old StartTimeUtc by @lahma in #2909
  • Fix DailyTimeIntervalTrigger mutating StartTimeUtc during fire time computation by @lahma in #2906
  • Fix DisallowConcurrentExecution jobs running simultaneously in cluster by @lahma in #2908
  • Fix DoNothing misfire policy skipping fire times within threshold by @lahma in #2912
  • Fix false cluster recovery causing DisallowConcurrentExecution violation by @lahma in #2915
  • Fix DailyTimeIntervalTrigger RepeatCount to apply per day (#1633) by @lahma in #2918
  • Add DST spring-forward tests for daily, monthly, and EU timezone intervals by @lahma in #2919
  • Fix DailyTimeIntervalTrigger extra fire during DST fall-back by @lahma in #2917
  • Fix SQLite database is locked errors (#2323) by @lahma in #2923
  • Fix PauseJobs/ResumeJob interaction bug in RAMJobStore by @lahma in #2893
  • Add MySQL FORCE INDEX hints for slow trigger queries by @lahma in #2894
  • Fix misfired blocked triggers not being deleted from database by @Copilot in #2820
  • Fix AddJob/AddTrigger ambiguous references without removing overloads (#2795) by @lahma in #2925
  • Add CronExpression DST regression tests (#2156) by @lahma in #2927
  • Fix scheduler thread stuck after system clock jumps backward (#1508) by @lahma in #2929
  • Fix AsyncLocal flow from IJobFactory.NewJob to IJob.Execute (#1528) by @lahma in #2930
  • Fix RAMJobStore.TriggersFired skipping triggers causing wrong job execution (#1386) by @lahma in #2933
  • Enable NUnit parallel test fixture execution by @lahma in #2936
  • Fix GetTriggerState returning Complete instead of Blocked for executing triggers (#2255) by @lahma in #2935
  • Fix unit tests failing on DST transition days by @lahma in #2941
  • Fix RefireImmediately with JobChainingJobListener firing chain prematurely (#663) by @lahma in #2940
  • Fix FIRED_TRIGGERS not cleaned up on job deletion mid-execution (#1696) by @lahma in #2945
  • Restore DB trigger fields for custom (blob) triggers by @lahma in #2949
  • Backport trigger type switch test coverage to 3.x by @lahma in #2948
  • Fix idleWaitTime of zero silently ignored instead of throwing (#1394) by @lahma in #2950
  • Fix RemoteScheduler ignoring local quartz.scheduler.instanceName (#313) by @lahma in #2953
  • Fix DedicatedThreadPool threads leaking on scheduler shutdown by @lahma in #2954
  • Fix TriggersFired not retrying transient database errors by @lahma in #2952
  • Fix PreviousFireTimeUtc reset to null on restart with OverWriteExistingData (#1834) by @lahma in #2957
  • Fix SimpleTrigger f...
Read more

Quartz.NET 3.16.1

Choose a tag to compare

@lahma lahma released this 04 Mar 21:42
c77bb2e

This release hopefully fixes the hiccup we had with the versioning/packaging of the new Dashboard package.

What's Changed

  • Add missing packaging and publish for Quartz.Dashboard by @lahma in #2853
  • Fix CronTrigger incorrectly scheduling when end date is in the past by @jafin in #2856

Full Changelog: v3.16.0...v3.16.1

Quartz.NET 3.16.0

Choose a tag to compare

@lahma lahma released this 01 Mar 20:25

On top of great community fixes, this release also brings the experimental Dashboard into action. You can enable it on NET 8 and later, see documentation for details.

What's Changed

  • Add tests for AddJob, AddTrigger, ScheduleJob and AddCalendar by @bdovaz in #2794
  • Use NET 10 SDK and update projects to use NET 10 by @lahma in #2800
  • Handle ObjectDisposedException when canceling jobs by @lahma in #2811
  • Fix trigger ERROR state during shutdown - release acquired triggers gracefully by @lahma in #2812
  • fix (CronExpression): Throw Exception for invalid cron expressions by @jafin in #2836
  • Convert solution file to slnx format by @lahma in #2840
  • Fix XML scheduling plugin to log errors and notify listeners on parsing failures by @jafin in #2839
  • Add IdleWaitTime property to SchedulerBuilder (#2835) by @lahma in #2842
  • Fix CronExpression not always returning correct next schedule for weekdayonly and month flags by @jafin in #2838
  • fix(CalendarIntervalTriggerImpl): infinite loop bug in CalendarIntervalTriggerImpl by @jafin in #2837
  • Fix GetScheduler(name) returning null before default scheduler initialization by @jafin in #2845
  • Port DirectoryScanJob listener DI support to 3.x by @lahma in #2851
  • Backport Quartz.Dashboard to 3.x (in-process only) by @lahma in #2852

Full Changelog: v3.15.1...v3.16.0

Quartz.NET 3.15.1

Choose a tag to compare

@lahma lahma released this 26 Oct 15:27

What's Changed

  • Fix StdScheduler.Interrupt not interrupting all identified jobs (#2788) by @unageek
  • Add IServiceProvider overloads for AddJob, AddTrigger, ScheduleJob, and AddCalendar (#2787) by @bdovaz

New Contributors

Full Changelog: v3.15.0...v3.15.1

Quartz.NET 3.15.0

Choose a tag to compare

@lahma lahma released this 03 Aug 18:08

What's Changed

  • allow to execute a job if remaining day time equals interval by @sherlock1982 in #2726
  • Switch to file-scoped namespaces by @lahma in #2750
  • Refactor PostgreSQL script to lower case qrtz_simprop_triggers table … by @hannes-justsolve in #2753
  • Fix IDriverDelegate XML documentation for SelectTriggerToAcquire by @lahma in #2757
  • Interrupt the scheduler thread when shutdown is occurring by @lahma in #2758
  • Validate that cron expression has at most 7 tokens by @lahma in #2760
  • When storing job with replace check update count returned by @lahma in #2761
  • Introduce JobExecutionProcessException to provide more context by @lahma in #2762
  • Fix DailyTimeIntervalTrigger.RepeatInterval setter validation by @lahma in #2763
  • Fix CronExpression increment error messages by @lahma in #2764
  • Implement CronExpression.GetTimeBefore() by @lahma in #2765
  • Optimize cron parsing by @lahma in #2769
  • Preserve Dates in Newtonsoft.Json NameValueCollectionConverter by @lahma in #2770

New Contributors

Full Changelog: v3.14.0...v3.15.0

Quartz.NET 3.14.0

Choose a tag to compare

@lahma lahma released this 08 Mar 11:30
e93242c

What's Changed

  • Change DirtyFlagMap.Add to use AllowNullAttribute instead of MaybeNullAttribute by @fbenoit-coexya in #2660
  • Don't transition triggers to error state on scheduler shutdown/disposal by @lahma in #2704
  • Handle hosted service stopping during startup gracefully by @lahma in #2705
  • Upgrade packages by @lahma in #2706
  • Add NET 8 and NET 9 targets, remove NET 6 target by @lahma in #2707

New Contributors

Full Changelog: v3.13.1...v3.14.0