尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Quartz.NET 3.6.2 修复解读:持久化 Job Store 中 DisallowConcurrentExecution 标志读取链路的回归与修复
任务调度后端【免费下载链接】quartznetQuartz Enterprise Scheduler .NET项目地址https://gitcode.com/gh_mirrors/qu/quartznet点击查看免费下载导读Quartz.NET 3.6.2 是一个典型的 fix to a fix release对修复的修复3.6.1 在修复DisallowConcurrentExecution()配置物化问题的同时向持久化 Job StoreADO JobStore引入了一处回归导致从数据库读取 Job 详情SqlSelectJobDetail时丢失IS_NONCONCURRENT列进而破坏了基于该标志的 Job 选择逻辑。本文以官方发布说明为骨架结合当前仓库源码完整还原该回归的产生背景、IS_NONCONCURRENT在持久化链路中的关键作用、3.6.2 的修复方式及其测试验证帮助使用持久化 Job Store 的读者理解升级 3.6.2 的必要性与原理。版本背景从 3.6.1 到 3.6.2 的 fix to a fix官方发布说明docs/_posts/2023-02-25-quartznet-3.6.2-released.md开篇即点明This is fix to a fix release, 3.6.1 introduced a regression to job selection logic when using persistent job store.即3.6.2 是 3.6.1 的修复版3.6.1 在使用持久化 Job Store 时引入了 Job 选择逻辑的回归。要理解这次回归需要先回顾 3.6.1 做了什么。3.6.1 的发布说明docs/_posts/2023-02-25-quartznet-3.6.1-released.md列出三项修复Add missing disallow concurrency materialization for jobs (#1923)——修复了使用 Job Builder 的DisallowConcurrentExecution()配置并发禁止、但 Job 类型本身没有标注DisallowConcurrentExecutionAttribute属性时该标志没有被正确物化持久化的问题Allow accessing the wrapped scoped job instance from job execution context (#1917)JobDiagnosticsWriter can throw error when writing context data (#1191)。也就是说3.6.1 补上了写入端的物化逻辑让DisallowConcurrentExecution()配置能被持久化到数据库但同一个修复过程中读取端的 SQL 语句SqlSelectJobDetail在改动列清单时漏掉了IS_NONCONCURRENT列形成了写进去却读不出来的不对称从而在持久化 Job Store 场景下破坏了 Job 选择逻辑。3.6.2 的唯一修复项正是针对这个缺口Fix SqlSelectJobDetail to include IS_NONCONCURRENT (#1927)回归的本质持久化 Job Store 的 Job 选择逻辑依赖IS_NONCONCURRENTQuartz.NET 的持久化 Job StoreAdoJobStore将 Job 与 Trigger 的元数据存放在数据库表中。其中QRTZ_JOB_DETAILSJOB_DETAILS表保存 Job 的属性快照包括两个行为标志列它们记录了 Job 在存储时刻的两个关键属性IS_NONCONCURRENT——对应IJobDetail.ConcurrentExecutionDisallowed即是否禁止并发执行来源通常是[DisallowConcurrentExecution]特性或JobBuilder.DisallowConcurrentExecution()IS_UPDATE_DATA——对应IJobDetail.PersistJobDataAfterExecution执行后是否持久化 JobDataMap 更新。这两个标志的列定义可见于各数据库的建表脚本例如 src/Quartz/Impl/AdoJobStore/Schema/create_sqlServer.sql 中QRTZ_JOB_DETAILS表包含IS_DURABLE、IS_NONCONCURRENT、IS_UPDATE_DATA、REQUESTS_RECOVERY等列QRTZ_FIRED_TRIGGERS已触发记录表中也冗余了IS_NONCONCURRENT、REQUESTS_RECOVERY列见同文件 L197-L201以便调度器在恢复、并发判定时不依赖 Job 程序集即可读取标志。仓库根目录下同样提供全量建表脚本 database/tables/tables_sqlServer.sql以及 MySQL、SQLite、Oracle、PostgreSQL、Firebird 各方言版本。为什么Job 选择逻辑需要这两个标志从源码看ConcurrentExecutionDisallowed至少在两个关键决策点被使用添加 Trigger 时的状态判定AdoJobStoreBase.AddTrigger需要依据IJobDetail.ConcurrentExecutionDisallowed决定新 Trigger 进入WAITING还是BLOCKED状态。源码注释src/Quartz/Impl/AdoJobStore/StdAdoConstants.cs明确指出这两个标志必须在没有 Job 的 CLR 类型的情况下也能回答否则在一个无法加载该 Job 程序集如集群中的其他节点的进程中回退到从类型反射读取属性会得到false导致非并发 Job 被误判为允许并发即 #3705 场景。Trigger 获取Acquire时的集群安全并发检查src/Quartz/Impl/AdoJobStore/AdoJobStoreBase.Acquisition.cs 中当job.ConcurrentExecutionDisallowed为真时会通过Delegate.IsJobCurrentlyExecuting查询FIRED_TRIGGERS表若该 Job 已在其他节点执行则直接拒绝本次触发——这是跨节点禁止并发的核心保障。若读取端丢掉了IS_NONCONCURRENT该检查将被跳过或错误判断。由此可见IS_NONCONCURRENT的缺失不是简单的少查一列而是会让持久化模式下禁止并发执行这一语义整体失效——这正是 3.6.1 回归被称为job selection logic regression的原因。修复对象SqlSelectJobDetail与共享列清单3.6.2 的修复落在StdAdoConstants.SqlSelectJobDetail上。当前仓库中该语句的定义src/Quartz/Impl/AdoJobStore/StdAdoConstants.cs为public static readonly string SqlSelectJobDetail Invariant($SELECT {JobDetailSelectColumns} FROM {TablePrefixSubst}{AdoConstants.TableJobDetails} WHERE {AdoConstants.ColumnSchedulerName} {SqlParameters.SchedulerName} AND {AdoConstants.ColumnJobName} {SqlParameters.JobName} AND {AdoConstants.ColumnJobGroup} {SqlParameters.JobGroup});关键的工程细节在于SqlSelectJobDetail与批量查询SqlSelectJobDetailsByKeysPrefix共享同一个列清单常量JobDetailSelectColumnssrc/Quartz/Impl/AdoJobStore/StdAdoConstants.csprivate const string JobDetailSelectColumns ${AdoConstants.ColumnJobName},{AdoConstants.ColumnJobGroup},{AdoConstants.ColumnDescription},{AdoConstants.ColumnJobClass},{AdoConstants.ColumnIsDurable},{AdoConstants.ColumnRequestsRecovery},{AdoConstants.ColumnJobDataMap},{AdoConstants.ColumnIsNonConcurrent},{AdoConstants.ColumnIsUpdateData};该清单现在完整包含IS_NONCONCURRENT与IS_UPDATE_DATAColumnIsNonConcurrent、ColumnIsUpdateData的常量定义见 src/Quartz/Impl/AdoJobStore/AdoConstants.cs。源码注释特别强调了两点约束单查询与批量查询共用列清单防止两条读取路径漂移drift apart——3.6.1 的回归正是这一类漂移的典型列顺序即序数ordinal契约JOB_DATA位于位置 6行读取器ReadJobDetail在CommandBehavior.SequentialAccess模式下按序读取列因此新列只能追加到清单末尾绝不能插入中间见 src/Quartz/Impl/AdoJobStore/StdAdoConstants.cs。读取链路从数据库行到IJobDetailSqlSelectJobDetail由StdAdoDelegate.SelectJobDetail执行src/Quartz/Impl/AdoJobStore/StdAdoDelegate.Jobs.cs执行时以SequentialAccess模式读取结果集并交给共享的ReadJobDetail方法src/Quartz/Impl/AdoJobStore/StdAdoDelegate.Jobs.cs构造IJobDetailjobBuilder.DisallowConcurrentExecution(GetBooleanFromDbValue(rs.GetValue(ordinals.IsNonConcurrent))) .PersistJobDataAfterExecution(GetBooleanFromDbValue(rs.GetValue(ordinals.IsUpdateData)));这里正是修复生效的落点从行的IS_NONCONCURRENT列直接读出布尔值通过JobBuilder.DisallowConcurrentExecution(bool)src/Quartz/JobBuilder.cs物化到 Job Detail 上而不是回退到反射读取类型上的特性。JobDetailRowOrdinalssrc/Quartz/Impl/AdoJobStore/StdAdoDelegate.Jobs.cs预先解析各列序数避免逐行重复GetOrdinal。与SelectJobDetail平行的还有另一条更轻量的读取路径StdAdoDelegate.SelectJobForTriggersrc/Quartz/Impl/AdoJobStore/StdAdoDelegate.Jobs.cs它执行SqlSelectJobForTrigger同样直接以DisallowConcurrentExecution(GetBooleanFromDbValue(rs[AdoConstants.ColumnIsNonConcurrent]))从行中读取标志注释明确写道列是 Job 在存储时刻属性的记录一个无法加载该类从而按类型反射会得到 false的进程不应被告知一个非并发 Job 允许并发#3705。AdoJobStoreBase的多处操作如 AdoJobStoreBase.Store.cs、L681、L812都依赖这条路径在不加载 Job 类型loadJobType: false的情况下拿到准确的标志值。一句话总结修复闭环写入端3.6.1#1923保证DisallowConcurrentExecution()配置被持久化到IS_NONCONCURRENT读取端3.6.2#1927保证SqlSelectJobDetail把该列重新读回IJobDetail.ConcurrentExecutionDisallowed。两个版本合在一起才构成完整的物化—读取链路。ConcurrentExecutionDisallowed在调度链路中的关键作用为了理解为什么这一列如此关键可以从调度执行的主链路看它的三个使用场景Trigger 状态机AdoJobStoreBase.AddTrigger依据ConcurrentExecutionDisallowed决定新 Trigger 是WAITING还是BLOCKED。标志丢失意味着非并发 Job 的 Trigger 不会被置为BLOCKED并发执行保护从源头失效。集群安全的获取检查如前述AdoJobStoreBase.Acquisition.cs 在获取 Trigger 时对禁止并发的 Job 查询FIRED_TRIGGERS发现已有实例在执行则拒绝本次触发从而跨节点保证同一时刻只有一个实例。这里读到的job正是来自SelectJobForTrigger/SelectJobDetail的行数据。执行完成/恢复路径AdoJobStoreBase.Completion.cs中 Job 执行完成后同样依据jobDetail.ConcurrentExecutionDisallowed决定是否解除同 Job 其他 Trigger 的BLOCKED状态src/Quartz/Impl/AdoJobStore/AdoJobStoreBase.Completion.cs。可见ConcurrentExecutionDisallowed贯穿添加 Trigger → 获取 Trigger → 执行完成的完整生命周期。任何一处读取到错误值都会让禁止并发执行这一 Job 配置在持久化部署尤其是集群中静默失效。测试验证列清单被测试锁定仓库的单元测试将SqlSelectJobDetail的列清单锁定为硬性契约。src/Quartz.Tests.Unit/Impl/AdoJobStore/StdAdoDelegateTest.cs 中构造的预期 SQL 明确包含REQUESTS_RECOVERY, JOB_DATA, IS_NONCONCURRENT, IS_UPDATE_DATA FROM QRTZ_JOB_DETAILS ...即当前测试期望的 SELECT 列集合正是修复后包含IS_NONCONCURRENT与IS_UPDATE_DATA的完整清单。此外持久化标志的从列读取而非反射语义还有专门的测试覆盖src/Quartz.Tests.Unit/Impl/AdoJobStore/SelectJobForTriggerFlagsSqliteTest.cs 断言ConcurrentExecutionDisallowed为真并注明IS_NONCONCURRENT是 Job 存储时特性的记录无需加载特性所在程序集即可读取src/Quartz.Tests.Unit/Impl/AdoJobStore/AdministrationNodeSqliteTest.cs 同样验证了该语义并在 L265-L268 的注释中警告若改为从类型推导标志会在一份无法看到 Job 类的 Job Detail 拷贝上错误清除IS_NONCONCURRENT——正是 3.6.1 回归要避免的静默损坏。升级建议与验证方法对于使用持久化 Job StoreADO JobStore无论单机还是集群的用户强烈建议从 3.6.1 升级到 3.6.2特别是通过JobBuilder.DisallowConcurrentExecution()而非[DisallowConcurrentExecution]特性配置并发禁止、且依赖持久化存储恢复 Job 元数据的场景。3.6.1 下这类 Job 在持久化读取后可能丢失禁止并发标志导致并发保护失效本修复不涉及数据库 Schema 变更IS_NONCONCURRENT列自 2.x 起就存在于各方言建表脚本中例如 src/Quartz/Impl/AdoJobStore/Schema/create_sqlite.sql因此无需执行迁移脚本升级程序集后重启即可生效升级后建议按仓库测试同等的思路做一次冒烟验证在持久化 Job Store 下创建一个通过JobBuilder.DisallowConcurrentExecution()配置、且类型上无特性标注的 Job触发一次执行后重启调度器再触发第二次确认两次执行不会并发重叠也可直接查阅 SelectJobForTriggerFlagsSqliteTest.cs 了解标志持久化—读取的断言方式。小结Quartz.NET 3.6.2 表面上只是一个为修复而修复的小版本但其背后是一堂关于持久化状态一致性的工程课Job 的行为标志ConcurrentExecutionDisallowed、PersistJobDataAfterExecution必须以数据库列作为唯一事实来源写入端与读取端必须共享同一列契约并且由测试锁定该契约——正如JobDetailSelectColumns共享常量与其注释所强调的那样。对于任何以持久化方式运行 Quartz.NET 的集群升级到 3.6.2 意味着禁止并发执行语义在完整生命周期内不再存在读写断点。赞分享任务调度后端【免费下载链接】quartznetQuartz Enterprise Scheduler .NET项目地址https://gitcode.com/gh_mirrors/qu/quartznet点击查看免费下载相关推荐Mosquitto 1.4.14 发布解读持久化数据保存回归的修复与规避升级指南Mosquitto 1.4.14 发布解读持久化数据保存回归的修复与规避升级指南 Mosquitto 1.4.14 是一次针对持久化功能的紧急 bugfix物联网消息队列后端Pillow 4.1.1 修复解读EXIF 中的 DPI 读取回归与 CPython 3.6 二进制兼容性Pillow 4.1.1 修复解读EXIF 中的 DPI 读取回归与 CPython 3.6 二进制兼容性 本篇技术指南围绕 Pillow 4.1.1 发布说图像处理计算机视觉RVC 变声教程从装环境到转出第一个 AI 音色5 步跑通RVC 变声教程从装环境到转出第一个 AI 音色5 步跑通 RVCRetrieval based Voice Conversion WebUI检索式语音人工智能AI 应用语音音频深度学习上一篇Docker-Android系统 requirements详解硬件与软件要求下一篇Tiny Care Terminal社区资源有用的插件和第三方工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED

相关推荐

现代 JavaScript 教程精解:反引号与 `${...}` 字符串插值——从“字符串的反引号“练习题看模板字面量机制

现代 JavaScript 教程精解:反引号与 `${...}` 字符串插值——从“字符串的反引号“练习题看模板字面量机制

文档教程前端 【免费下载链接】zh.javascript.info 现代 JavaScript 教程(The Modern JavaScript Tutorial),以最新的 ECMAScript 规范为基准,通过简单但足够详细的内容,为你讲解从基础到高阶的 JavaScript 相关知识。…

📅 2026/10/6 1:54:43
learnxinyminutes-docs 实战:15 分钟上手 Emacs Lisp——从 sexp 求值到 buffer 文本处理

learnxinyminutes-docs 实战:15 分钟上手 Emacs Lisp——从 sexp 求值到 buffer 文本处理

文档教程 【免费下载链接】learnxinyminutes-docs Code documentation written as code! How novel and totally my idea! 项目地址: https://gitcode.com/gh_mirrors/le/learnxinyminutes-docs 点击查看 免费下载 本篇基于 learnxinyminutes-docs 仓库中的 es/eli…

📅 2026/10/6 1:54:43
MuPDF C 编码风格指南:命名、缩进与引用计数规则解析

MuPDF C 编码风格指南:命名、缩进与引用计数规则解析

图形学图像处理 【免费下载链接】mupdf mupdf mirror 项目地址: https://gitcode.com/gh_mirrors/mu/mupdf 点击查看 免费下载 本文以 MuPDF 官方编码风格文档为骨架,系统梳理其 C 库(fitz 公共层与 pdf/xps 等解释器层)在缩进、…

📅 2026/10/6 1:54:43
MORE NEWS

更多资讯

📰

(论文速读)LogSAD:无需训练的结构异常与逻辑异常统一检测

论文题目:Towards Training-free Anomaly Detection with Vision and Language Foundation Models(迈向基于视觉与语言基础模型的免训练异常检测) 会议:CVPR 2025 摘要:异常检测在工业质量检测等真实场景中具有重要价…

📰

Psychol Med:有抑郁症状的老年人中结构-功能连接耦合的改变

本篇文献发表在Psychological Medicine杂志。所发布内容旨在与大家分享学术新知,促进交流学习,版权归原作者或原出处所有,感谢各位学者的辛勤付出与研究成果。1. 引言抑郁症状和重度抑郁障碍在老年人中普遍存在,受到慢性疾病、睡眠…

📰

PDF 论文处理器 — 优势与功能文档

PDF 论文处理器 — 优势与功能文档文档性质:核心功能总结、设计亮点提炼、相对通用方案的差异化优势1. 核心功能概览本工具围绕"把网安科研 PDF 变成 Dify 可用的高质量知识块"这一目标,提供以下端到端能力:功能说明对应模块智能 P…

📰

图数据库系列 · 第 04 篇部署实操:内网集群落地

从离线安装到 Neo4j 因果集群 目 录 一、导读与节点规划 二、单机离线安装 2.1 依赖与解压 2.2 基础配置 neo4j.conf 2.3 启动与验证 三、因果集群部署 3.1 集群要求 3.2 集群发现配置 3.3 启动与验证 四、运维与一键部署脚本 4.1 常用运维命令 4.2 一键部署脚本骨架 五、本篇…

📰

图数据库系列 · 第 05 篇选型对比:图库与相邻方案

Neo4j / NebulaGraph / 关系库横评 目 录 一、导读 二、Neo4j 社区版与企业版 2.1 版本差异 2.2 Infinigraph 三、三大主流图库对比 3.1 定位差异 3.2 其他图库 3.3 选型结论 四、图库与关系库 4.1 差异 4.2 何时用图库 4.3 融合而非替代 五、综合对比与选择建议 5.1 选择逻辑…

📰

clip 数据驱动尺寸映射指南:使用 measure-map 的 linear 映射为散点图点大小绑定输入数据

【免费下载链接】clip Create charts from the command line 项目地址: https://gitcode.com/gh_mirrors/cli/clip 点击查看 免费下载 size-map(测量映射)是 clip 命令行图表工具中用于把输入数据值映射为排版尺寸单位的核心机制。当你想创建…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬