I have a problem with using Hibernate and have isolated it to the setting QueryCacheEnabled=true. With this set to false the bug is not present and the application works flawlessly. When QueryCacheEnabled is set to true the error occurs. Below is a description of how things work. The error, and my suspicions follow at the end.
I have spent some considerable time looking into this issue, and believe I have narrowed it down substantially
Hibernate version: 2.1.6
JBoss version: 4.0.0
In summary: Hibernate runs within JBoss application server as a registered MBean. It uses JBossTree cache as its cache provider, which is also registereas an MBean. See below.
Code:
<mbean code="org.jboss.hibernate.jmx.Hibernate" name="jboss.har:service=Hibernate">
<depends>jboss:service=TransactionManager</depends>
<depends>jboss.jca:service=DataSourceBinding,name=b3xt_ds_pirate</depends>
<depends>jboss.cache:service=TreeCache</depends>
<attribute name="DatasourceName">java:/b3xt_ds_pirate</attribute>
<attribute name="SessionFactoryName">java:/hibernate/HibernateFactory</attribute>
<attribute name="Dialect">net.sf.hibernate.dialect.SQLServerDialect</attribute>
<attribute name="CacheProviderClass">MyJBossTreeCacheProvider</attribute>
<attribute name="SessionFactoryInterceptor">caw.b3xt.domain.Interceptor</attribute>
<attribute name="HarUrl">${jboss.server.home.dir}/lib/pirate_hib.jar</attribute>
<attribute name="QuerySubstitutions">true 1, false 0, yes 'Y', no 'N'</attribute>
<attribute name="QueryCacheEnabled">false</attribute>
<mbean code="org.jboss.cache.TreeCache" name="jboss.cache:service=TreeCache">
<depends>jboss:service=Naming</depends>
<depends>jboss:service=TransactionManager</depends>
<attribute name="JndiName">java:/TreeCache</attribute>
<attribute name="TransactionManagerLookupClass">org.jboss.cache.JBossTransactionManagerLookup</attribute>
<attribute name="IsolationLevel">READ_COMMITTED</attribute>
<attribute name="CacheMode">REPL_ASYNC</attribute>
<attribute name="UseReplQueue">false</attribute>
<attribute name="ReplQueueInterval">1000</attribute>
<attribute name="ReplQueueMaxElements">20</attribute>
<attribute name="FetchStateOnStartup">true</attribute>
<attribute name="InitialStateRetrievalTimeout">15000</attribute>
<attribute name="SyncReplTimeout">10000</attribute>
<attribute name="LockAcquisitionTimeout">15000</attribute>
<attribute name="EvictionPolicyClass"></attribute>
<attribute name="ClusterName">TreeCache-Cluster</attribute>
<attribute name="ClusterConfig">
<config>
<UDP mcast_addr="228.1.2.3" mcast_port="12233" ip_ttl="64" ip_mcast="true" mcast_send_buf_size="150000" cast_recv_buf_size="80000"
ucast_send_buf_size="150000" ucast_recv_buf_size="80000" bind_addr="primary.nic.b3xt.host" loopback="true"/>
<PING timeout="2000" num_initial_members="3" />
<MERGE2 min_interval="10000" max_interval="20000"/>
<FD_SOCK/>
<VERIFY_SUSPECT timeout="1500" up_thread="false" down_thread="false"/>
<pbcast.NAKACK gc_lag="50" retransmit_timeout="600,1200,2400,4800" max_xmit_size="8192" up_thread="false" down_thread="false"/>
<UNICAST timeout="600,1200,2400" window_size="100" min_threshold="10" down_thread="false"/>
<pbcast.STABLE desired_avg_gossip="20000" up_thread="false" down_thread="false"/>
<FRAG frag_size="8192" down_thread="false" up_thread="false"/>
<pbcast.GMS join_timeout="5000" join_retry_timeout="2000" shun="true" print_local_addr="true"/>
<pbcast.STATE_TRANSFER up_thread="true" down_thread="true"/>
</config>
</attribute>
The "MyJBossTreeCacheProvider" hibernate cache provider class is a copy of the Hibernate supplied "net.sf.hibernate.cacheTreeCacheProvider", but looks up the Cache in JNDI, rather than constructing the Cache on the fly. This allows the cache to be configured and managed as a regular MBean.
All hibernate access is done within Stateless session EJB's. This bean method has been declared with a Transaction Attribute of "Required", meaning a JTA transaction is created, and managed by the container. The above MBean configuration ensures that the Cache and Hibernate act within the semantics of JTA transactions.
I have ensured all hibernate mapping documents have set cache usage as either "transactional" or as "read-only"
The EJB's obtains a hibernate session by using the JBoss 4.0.0 (final release) org.jboss.hibernate.session.HibernateContext.getSession( String name ); method. And do not flush, commit, or otherwise close the session, leaving this up to JTA.
The EJB's are invoked via a Structs action class, which calls the bean, performs a hibernate update, stores the result in the Session. A JSP then presents the information. This architecture should be fairly standard, and works perfectly.
QueryCacheEnabled=trueAfter setting QueryCacheEnabled=true, The bug occurs on the second request to a URL, wheras worked perfectly before. The error produced is as follows.
Code:
2004-10-18 14:29:34,734 ERROR [net.sf.hibernate.impl.SessionImpl] Could not synchronize database state with session
net.sf.hibernate.cache.CacheException: org.jboss.cache.lock.TimeoutException: lock could not be acquired after 15000 ms. Lock map ownership Read lock owners: []
Write lock owner: <primary:3279>:15
at caw.util.hibernate.cache.JBossTreeCache.put(JBossTreeCache.java:75)
at net.sf.hibernate.cache.UpdateTimestampsCache.preinvalidate(UpdateTimestampsCache.java:42)
at net.sf.hibernate.impl.SessionImpl.executeAll(SessionImpl.java:2413)
at net.sf.hibernate.impl.SessionImpl.execute(SessionImpl.java:2368)
at net.sf.hibernate.impl.SessionImpl.flush(SessionImpl.java:2236)
...
However I believe the error was caused by a previous URL request creating a lock in the cache which was never released. Remember the error occurs on the second invocation to the URL.
To diagnose the problem enabled DEBUG level logging in JBoss for Hibernate, and JBossCache. When the application is working (QueryCacheEnabled=false) the following is a dump of the DEBUG log.
Code:
2004-10-18 15:16:23,718 DEBUG [net.sf.hibernate.impl.SessionImpl] post flush
2004-10-18 15:16:23,718 DEBUG [net.sf.hibernate.engine.CacheSynchronization] transaction before completion callback
2004-10-18 15:16:23,718 DEBUG [net.sf.hibernate.impl.SessionImpl] closing session
2004-10-18 15:16:23,718 DEBUG [net.sf.hibernate.impl.SessionImpl] disconnecting session
2004-10-18 15:16:23,718 DEBUG [net.sf.hibernate.engine.CacheSynchronization] transaction after completion callback, status: 3
2004-10-18 15:16:23,718 DEBUG [net.sf.hibernate.impl.SessionImpl] transaction completion
2004-10-18 15:16:23,718 DEBUG [org.jboss.cache.interceptors.ReplicationInterceptor] runPreparePhase(): run remote prepare with async mode: true commit: true and coordinator: primary:3387
2004-10-18 15:16:23,718 DEBUG [org.jboss.cache.TreeCache] callRemoteMethods(): valid members are [smtpserver:4076]
2004-10-18 15:16:23,718 DEBUG [org.jboss.cache.TreeCache] commit(): called to commit cache with gtx <primary:3387>:15
2004-10-18 15:16:23,734 DEBUG [org.jboss.cache.TreeCache] commit(): releasing lock
2004-10-18 15:16:23,734 DEBUG [org.jboss.cache.lock.IdentityLock] release(): lock map owner <primary:3387>:15 not found. Could have been released.
2004-10-18 15:16:23,734 DEBUG [org.jboss.cache.TreeCache] commit(): releasing lock read owners=[<primary:3387>:15]
2004-10-18 15:16:23,734 DEBUG [org.jboss.cache.TreeCache] commit(): releasing lock write owner=<primary:3387>:15
2004-10-18 15:16:23,734 DEBUG [org.jboss.cache.TreeCache] commit(): releasing lock read owners=[<primary:3387>:15]
2004-10-18 15:16:23,734 DEBUG [org.jboss.cache.TreeCache] commit(): removing local transaction key TransactionImpl:XidImpl[FormatId=257, GlobalId=cw-scgymo62h4gl/27, BranchQual=, localId=27]
2004-10-18 15:16:23,750 DEBUG [JSP] JSP Start
The last line of the log is where my JSP takes over, notice that the last thing the cache does is to do a commit. When the application is Not working (QueryCacheEnabled=true) the following appears in the log.
Code:
2004-10-18 14:49:01,000 DEBUG [net.sf.hibernate.impl.SessionImpl] post flush
2004-10-18 14:49:01,000 DEBUG [net.sf.hibernate.engine.CacheSynchronization] transaction before completion callback
2004-10-18 14:49:01,000 DEBUG [net.sf.hibernate.impl.SessionImpl] closing session
2004-10-18 14:49:01,000 DEBUG [net.sf.hibernate.impl.SessionImpl] disconnecting session
2004-10-18 14:49:01,000 DEBUG [org.jboss.cache.interceptors.ReplicationInterceptor] runPreparePhase(): run remote prepare with async mode: true commit: true and coordinator: primary:3342
2004-10-18 14:49:01,000 DEBUG [org.jboss.cache.TreeCache] callRemoteMethods(): valid members are [smtpserver:4076]
2004-10-18 14:49:01,015 DEBUG [org.jboss.cache.TreeCache] commit(): called to commit cache with gtx <primary:3342>:16
2004-10-18 14:49:01,015 DEBUG [org.jboss.cache.TreeCache] commit(): releasing lock
2004-10-18 14:49:01,015 DEBUG [org.jboss.cache.lock.IdentityLock] release(): lock map owner <primary:3342>:16 not found. Could have been released.
2004-10-18 14:49:01,015 DEBUG [org.jboss.cache.TreeCache] commit(): releasing lock read owners=[<primary:3342>:16]
2004-10-18 14:49:01,015 DEBUG [org.jboss.cache.TreeCache] commit(): releasing lock write owner=<primary:3342>:16
2004-10-18 14:49:01,015 DEBUG [org.jboss.cache.TreeCache] commit(): releasing lock write owner=<primary:3342>:16
2004-10-18 14:49:01,015 DEBUG [org.jboss.cache.TreeCache] commit(): releasing lock read owners=[<primary:3342>:16]
2004-10-18 14:49:01,015 DEBUG [org.jboss.cache.TreeCache] commit(): releasing lock write owner=<primary:3342>:16
2004-10-18 14:49:01,015 DEBUG [org.jboss.cache.TreeCache] commit(): releasing lock read owners=[<primary:3342>:16]
2004-10-18 14:49:01,015 DEBUG [org.jboss.cache.TreeCache] commit(): removing local transaction key TransactionImpl:XidImpl[FormatId=257, GlobalId=cw-scgymo62h4gl/26, BranchQual=, localId=26]
2004-10-18 14:49:01,015 DEBUG [net.sf.hibernate.engine.CacheSynchronization] transaction after completion callback, status: 3
2004-10-18 14:49:01,015 DEBUG [net.sf.hibernate.impl.SessionImpl] transaction completion
[b]2004-10-18 14:49:01,015 DEBUG [org.jboss.cache.TreeCache] _put(<primary:3342>:17, "//net/sf/hibernate/cache/UpdateTimestampsCache/UserList", item, 10981073410)
2004-10-18 14:49:01,015 DEBUG [org.jboss.cache.Node] acquired RL: fqn=//net/sf/hibernate/cache/UpdateTimestampsCache, owner=<primary:3342>:17, lock=read owners=[<primary:3342>:17]
2004-10-18 14:49:01,015 DEBUG [org.jboss.cache.Node] acquired WL: fqn=//net/sf/hibernate/cache/UpdateTimestampsCache/UserList, owner=<primary:3342>:17, lock=write owner=<primary:3342>:17
2004-10-18 14:49:01,015 DEBUG [org.jboss.cache.TreeCache] _put(<primary:3342>:17, "//net/sf/hibernate/cache/UpdateTimestampsCache/GroupUser", item, 10981073410)
2004-10-18 14:49:01,015 DEBUG [org.jboss.cache.lock.IdentityLock] caller already owns lock.
2004-10-18 14:49:01,015 DEBUG [org.jboss.cache.Node] acquired RL: fqn=//net/sf/hibernate/cache/UpdateTimestampsCache, owner=<primary:3342>:17, lock=read owners=[<primary:3342>:17]
2004-10-18 14:49:01,015 DEBUG [org.jboss.cache.Node] acquired WL: fqn=//net/sf/hibernate/cache/UpdateTimestampsCache/GroupUser, owner=<primary:3342>:17, lock=write owner=<primary:3342>:17[/b]
2004-10-18 14:49:01,031 DEBUG [JSP] JSP Start
Note the bolded section of the document, which is not present in the original log, this appears to be the QueryCache invalidation in the cache. However notice that there does not appear to be any commit logic at the end of the process. There fore I surmise, that locks are remaining open in the cache, and the next invocation of the URL, cannot obtain the locks it requires.
I have looked at the source code of hibernate, and believe the issue is in the area of the public void afterTransactionCompletion(boolean success) method in SessionImpl class. This method appears to be where query cache invalidation occurs, my guess based on the name of the method, it occurs after the JTA transaction has been committed, and as a consequence the JBossCache, which also uses JTA, has already done its commit. Therefore the locks generated from this call are never committed. This is also evidenced in the debug log above.
Shouldn