在一套集羣應用(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提供的文檔好像沒有說明事務的問題,難道真的只能好好看註釋了?