在一套集群应用(4个实例节点)启动的时候,应用调用了QuartzScheduler#scheduleJob初始化定时任务,会偶然触发JobPersistenceException: could not store trigger : unique constraint violdated.即多个节点同时scheduleJob的时候了违反约束条件.java
问题代码大概以下:数据库
public class MyQuartzConfig {
@Autowired
private Scheduler scheduler;
public void init() {
scheduler.scheduleJob(job, trigger);
}
}
复制代码
咱们都知道,Quartz集群模式是借用了数据库来实现竞争锁的,若是某个节点获取锁失败,会等待一段时间后再次尝试,直到超太重试次数抛出异常.上述出现违反约束条件确定是由于锁没有生效,换句话说事务没有生效spa
Quartz提供了两个持久化任务的实现类,分别是JobStoreCMT和JobStoreTXcode
JobStoreCMT的两个数据源怎么理解呢?根据官方注释能够看到,TxDataSource事务受容器管理,可应用于JTA等XA场合. 而NonTxDataSource事务不受容器管理,是Quartz自行管理.cdn
小结:CMT的NonTxDataSource便是JobStoreTX的数据源,Quartz获取connection后会设置autoCommit=false,由Quartz自行控制事务.blog
而JobStoreCMT的TxDataSource是额外添加的,为的是让用户可以控制这些事务,给用户更大的灵活性.事务
JobStore在不一样的场景下都会用到这两种数据源,分别是executeInLock和executeInNonManagedTXLock文档
当Spring接管Quartz后,状况会发生一些变化. 若是Spring提供的SchedulerFactoryBean设置了数据源,就会用LocalDataSourceJobStore来替代Quartz的JobStore类. it
看到这里应该能猜到缘由了,在咱们的例子里,调用scheduleJob方法使用的数据源是TxDataSource,而持久化类LocalDataSourceJobStore默认autoCommit=ture,在这种状况下其实咱们调用时是不存在事务的,天然就会出现文中的问题.io
没有事务的话给它加上事务就行了,因为咱们用到Spring-Quartz,加上Spring的事务就能解决问题
public class MyQuartzConfig {
@Autowired
private Scheduler scheduler;
@Transactional // 加上事务注解便可解决问题
public void init() {
scheduler.scheduleJob(job, trigger);
}
}
复制代码
Quartz提供JobStoreCMT是为了让用户调用Quartz的时候有事务控制(例如但愿多个Quartz实例同时成功或同时失败).
我也不知道之后怎么避免这类问题,Spring提供的文档好像没有说明事务的问题,难道真的只能好好看注释了?