Oracle application server使用Quartz JobStoreCMT遇到程序被锁问题

Oracle application server使用Quartz JobStoreCMT遇到程序被锁问题
由于业务需要将quartz的jobstore从JobStoreTx更新为JobStoreCMT,接着启动servlet时发现被锁住了。由于使用jobstorecmt我们使用了managed datasource,按照quartz文档的要求我也配置了non managed datasource,在quartz中什么是managed datasource和non managed datasource呢?

Managed datasource: 使用了cmt才有的概念,应该主要是job执行时还有一些其他和job有关的操作quartz会用到的datasource,这个datasource是由容器来管理的,也就是一般意义上我们使用的managed datasource,但是它还强调的是transaction是由容器或者用户自己的程序来控制(JTA)。quartz的job执行提供了一个UserTransaction的wrap。

Non managed datasource; JobStoreTX默认就是使用这个datasource,默认是dbcp的pool,这个non managed datasource不是指我们不能使用容器管理的datasource,也可以配置成容器管理的datasource的,但要注意的是transaction level是local的,transaction是由quartz控制的,quartz来完成transaction的语义和边界。


一开始的配置如下:
# The value of org.quartz.scheduler.instanceName
# can be any string
,  and has no meaning to the scheduler itself -
# but rather serves as a mechanism for client code to distinguish schedulers
# when multiple instances are used within the same program.  If you are using
# the clustering features
,  you must use the same name for every instance in 
# the cluster that is 'logically' the same Scheduler.
#
# NOTE: Especially for the application using LTS
,  the instanceName is different and 
#       specific per application to avoid the interruption between applications. For
#       example in TMS project the instanceName could be named as TMS_APP
,  in DMS
#       project the instanceName could be named as DMS_APP etc
,  the instanceName in
#       different projects must be different. For more details
,  you could refer to
#       the LTS installation guide.
org.quartz.scheduler.instanceName 
=  LTS
org.quartz.scheduler.rmi.export 
=  true
org.quartz.scheduler.rmi.registryHost 
=  localhost
org.quartz.scheduler.rmi.registryPort 
=   1100
org.quartz.scheduler.rmi.createRegistry 
=  as_needed
org.quartz.scheduler.wrapJobExecutionInUserTransaction 
=  true


org.quartz.threadPool.class 
=  org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount 
=   10
org.quartz.threadPool.threadPriority 
=   5
org.quartz.jobStore.misfireThreshold 
=   2592000000


org.quartz.jobStore.class 
=  org.quartz.impl.jdbcjobstore.JobStoreCMT


org.quartz.jobStore.driverDelegateClass 
=  org.quartz.impl.jdbcjobstore.oracle.OracleDelegate

org.quartz.jobStore.useProperties 
=  false
org.quartz.jobStore.dataSource 
=  myXADS
org.quartz.jobStore.nonManagedTXDataSource 
=  myDS
org.quartz.jobStore.tablePrefix 
=  QRTZ_
org.quartz.jobStore.isClustered 
=  false
org.quartz.jobStore.selectWithLockSQL 
=  

org.quartz.dataSource.myXADS.jndiURL 
=  jdbc/ltstxxatest

org.quartz.dataSource.myDS.driver 
=  oracle.jdbc.OracleDriver
org.quartz.dataSource.myDS.URL 
=  jdbc:oracle:thin:@shhpdv11: 1521 :WOSDB
org.quartz.dataSource.myDS.user 
=  sysint
org.quartz.dataSource.myDS.password 
=  sysint
org.quartz.dataSource.myDS.maxConnections 
=   5






datasource的配置:
     < managed-data-source  name ="LTSTxTestXADataSource"
        connection-pool-name
="LTSTxTestXAConnectionFactory"
        jndi-name
="jdbc/ltstxxatest"  tx-level ='global'  />
    
<!--  tx-level='global'  -->
    
< connection-pool  name ="LTSTxTestXAConnectionFactory" >
        
< connection-factory
            
factory-class ="oracle.jdbc.xa.client.OracleXADataSource"
            user
="sysint"  password ="sysint"
            url
="jdbc:oracle:thin:@shhpdv11:1521:WOSDB" >
        
</ connection-factory >
    
</ connection-pool >

但是发现servlet在启动时hold在那里了,跟了一下代码,发现quartz jobstoreCMT默认是使用的oracle db的TM锁,而jobstoreTX使用的是java的逻辑锁,于是登录到db中,查了一下,发现一个insert quartz_simple_triggers的DML操作申请了Qaurtz_Locks的表锁,而接着start quartz scheduler时也会去申请这个表锁,但是前面的DML操作一直都没有commit,所有quartz scheduler start时就被锁在那里了。然后试着将non managed datasource和managed datasource配成同一个datasource启动ok没有问题,但是正是因为配了同一个datasource可能下一次从pool中拿的connection的oracle session还是拥有那个锁资源的,锁资源没有被先前的connection释放掉,因为事务没有提交,connection的逻辑close不会释放session拥有的资源。这样又回到了原点,白试了一把,后来仔细跟了一把程序,发现autocommit是false,也没有地方显著的提交,scheduleJob和unscheduleJob都会有这种情况,本来以为只有job执行的时候会用到managed datasource,这样看来很多对job信息的处理的地方都用到了managed datasource,但是为什么quartz不帮我们wrap起来提交呢,控制transaction的任务交给了我们自己或container,交给我们自己是指我们需要自己用JTA transaction封装这些操作,交给container就需要把属性:

org.quartz.jobStore.dontSetAutoCommitFalse  =  true

但是quartz文档中提到下面这一段:

org.quartz.jobStore.dontSetAutoCommitFalse

False

Description: Setting this parameter to true tells Quartz not to call setAutoCommit(false) on connections obtained from the DataSource(s). This can be helpful in a few situations, such as if you have a driver that complains if it is called when it is already off. This property defaults to false because most drivers require that setAutoCommit(false) be called.


大概意思是大多数jdbc driver是不用去设置auto commit为true的,保持默认值是false就可以了,又看到了这篇文章:

http://forums.opensymphony.com/thread.jspa?threadID=14762&messageID=28963#28963

主要是说auto commit设成true会改变quartz transaction的语义,本来时多步操作提交的,现在变成了每步dml操作都会提交。但是如果不设auto commit在oracle jdbc中似乎没办法再提交这个transaction了,锁就不会被释放。于是找了找,找到了下面这篇文章:

http://forums.opensymphony.com/thread.jspa?threadID=365430&messageID=452688#452688

这篇文章也遇到了类似这样的问题,提交了trigger但是没有到db中,应该也是被锁住了。提出的解决办法就是把auto commit设成true。接着尝试了一下,顺利成功启动,trigger成功调度,job成功执行。表面上看似没有什么问题,但是一直还是有concern,因为我们这样设置会不会更改了quartz事务的语义,仔细看了quartz jobstore相关的代码,jobstorecmt中其实还有一个属性:

     //  Great name huh?
     protected   boolean  dontSetNonManagedTXConnectionAutoCommitFalse  =   false ;

文档中的描述:

org.quartz.jobStore.dontSetNonManagedTX ConnectionAutoCommitFalse

False

Description: This is the same as the property org.quartz.jobStore.dontSetAutoCommitFalse, except that it applies to the nonManagedTXDataSource.


默认值是false,所有non managed datasource都是用的这个值,auto commit是false,设置org.quartz.jobStore.dontSetAutoCommitFalse = false不会影响non managed datasource的transaction语义。这样想想把这个值设成true也不会有什么问题,不过进一步还是要进行大量的测试。所以oracle application server使用jobstorecmt后quartz的配置应该如下:

# The value of org.quartz.scheduler.instanceName
# can be any string
,  and has no meaning to the scheduler itself -
# but rather serves as a mechanism for client code to distinguish schedulers
# when multiple instances are used within the same program.  If you are using
# the clustering features
,  you must use the same name for every instance in 
# the cluster that is 'logically' the same Scheduler.
#
# NOTE: Especially for the application using LTS
,  the instanceName is different and 
#       specific per application to avoid the interruption between applications. For
#       example in TMS project the instanceName could be named as TMS_APP
,  in DMS
#       project the instanceName could be named as DMS_APP etc
,  the instanceName in
#       different projects must be different. For more details
,  you could refer to
#       the LTS installation guide.
org.quartz.scheduler.instanceName 
=  LTS
org.quartz.scheduler.rmi.export 
=  true
org.quartz.scheduler.rmi.registryHost 
=  localhost
org.quartz.scheduler.rmi.registryPort 
=   1101
org.quartz.scheduler.rmi.createRegistry 
=  as_needed
org.quartz.scheduler.wrapJobExecutionInUserTransaction 
=  true


org.quartz.threadPool.class 
=  org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount 
=   10
org.quartz.threadPool.threadPriority 
=   5
org.quartz.jobStore.misfireThreshold 
=   2592000000


org.quartz.jobStore.class 
=  org.quartz.impl.jdbcjobstore.JobStoreCMT


org.quartz.jobStore.driverDelegateClass 
=  org.quartz.impl.jdbcjobstore.oracle.OracleDelegate

org.quartz.jobStore.useProperties 
=  false
org.quartz.jobStore.dataSource 
=  myXADS
org.quartz.jobStore.nonManagedTXDataSource 
=  myDS
org.quartz.jobStore.tablePrefix 
=  QRTZ_
org.quartz.jobStore.isClustered 
=  false
org.quartz.jobStore.dontSetAutoCommitFalse 
=  true
org.quartz.jobStore.selectWithLockSQL 
=  

org.quartz.dataSource.myXADS.jndiURL 
=  jdbc/ltstxxatest


org.quartz.dataSource.myDS.driver 
=  oracle.jdbc.OracleDriver
org.quartz.dataSource.myDS.URL 
=  jdbc:oracle:thin:@shhpdv11: 1521 :WOSDB
org.quartz.dataSource.myDS.user 
=  sysint
org.quartz.dataSource.myDS.password 
=  sysint
org.quartz.dataSource.myDS.maxConnections 
=   5




你可能感兴趣的:(Oracle application server使用Quartz JobStoreCMT遇到程序被锁问题)