[jboss-user] [JBoss Cache: Core Edition] - Re: invalidation messages lost/ignored?

dukehoops do-not-reply at jboss.com
Thu Mar 26 18:17:31 EDT 2009


The possible solution I described above works for me. The code's below, I would still appreciate peer review and feedback, particularly on:
-whether option.setCacheModeLocal(true) is enough to ensure non-propagation of the remove call (to other cluster nodes)
-why was cache getting into this (indefinite) state and whether that's to be expected or a sign of a bug/greater problem


  | package com.doppelganger.framework.hibernate;
  | 
  | import com.doppelganger.framework.cache.DGEntityTransactionalAccess;
  | import javax.transaction.Transaction;
  | import org.hibernate.HibernateException;
  | import org.hibernate.StaleObjectStateException;
  | import org.hibernate.cache.CacheKey;
  | import org.hibernate.event.EventSource;
  | import org.hibernate.event.FlushEvent;
  | import org.hibernate.event.def.DefaultFlushEventListener;
  | import org.hibernate.impl.SessionFactoryImpl;
  | import org.hibernate.persister.entity.EntityPersister;
  | import org.slf4j.Logger;
  | import org.slf4j.LoggerFactory;
  | import org.springframework.beans.factory.annotation.Autowired;
  | import org.springframework.stereotype.Component;
  | import org.springframework.transaction.jta.JtaTransactionManager;
  | 
  | /**
  |  *
  |  * @author nikita
  |  */
  | @Component("cacheUpdatingFlushEventListener")
  | public class CacheUpdatingFlushEventListener extends DefaultFlushEventListener {
  | 
  |     final static private Logger LOG = LoggerFactory.getLogger(CacheUpdatingFlushEventListener.class);
  |     @Autowired
  |     private JtaTransactionManager jtaTransactionManager;
  | 
  |     @Override
  |     public void onFlush(FlushEvent event) throws HibernateException {
  |         try {
  |             super.onFlush(event);
  |         } catch (StaleObjectStateException ex) {
  | 
  |             //if possible, remove offending entity from 2nd level cache
  |             //remove offending ENTITY cache node (only locally - no cluster-wide propagation)
  | 
  |             Transaction tx = null;
  |             try {
  |                 SessionFactoryImpl sfImpl = (SessionFactoryImpl) event.getSession().getSessionFactory();
  |                 EntityPersister persister = sfImpl.getEntityPersister(ex.getEntityName());
  |                 if (persister.hasCache()) {
  |                     EventSource session = event.getSession();
  |                     CacheKey ck = new CacheKey(
  |                             ex.getIdentifier(),
  |                             persister.getIdentifierType(),
  |                             persister.getRootEntityName(),
  |                             session.getEntityMode(),
  |                             session.getFactory());
  | 
  | 
  |                     if (persister.getCacheAccessStrategy() instanceof DGEntityTransactionalAccess) {
  | 
  |                         LOG.debug("removing Entity cache node with key=" + ck + " after concurrency exception: " + ex);
  | 
  |                         tx = jtaTransactionManager.getTransactionManager().suspend();
  | 
  |                         DGEntityTransactionalAccess entityCacheAccessStrategy = (DGEntityTransactionalAccess) persister.getCacheAccessStrategy();
  |                         entityCacheAccessStrategy.remove(ck, true);
  |                     }
  |                 }
  |             } catch (Throwable t) {
  |                 LOG.error("error removing Entity cache node after detecting exception: " + ex + ". Proceeding to rethrow detected exception", t);
  |             } finally {
  |                 if (tx != null) {
  |                     try {
  |                         jtaTransactionManager.getTransactionManager().resume(tx);
  |                     } catch (Throwable t) {
  |                         LOG.error("failed to resume a tx - INVESTIGATE!", t);
  |                     }
  |                 }
  |                 //rethrow original exception
  |                 throw ex;
  |             }
  |         }
  |     }
  | }
  | 


  | package com.doppelganger.framework.cache;
  | 
  | import org.hibernate.cache.CacheException;
  | import org.hibernate.cache.CacheKey;
  | import org.hibernate.cache.jbc2.entity.EntityRegionImpl;
  | import org.hibernate.cache.jbc2.entity.TransactionalAccess;
  | import org.jboss.cache.config.Option;
  | 
  | /**
  |  *
  |  * @author nikita
  |  */
  | public class DGEntityTransactionalAccess extends TransactionalAccess {
  | 
  |     final private DGTransactionalAccessDelegate accessDelegate;
  | 
  |     public DGEntityTransactionalAccess(EntityRegionImpl region, DGTransactionalAccessDelegate delegate) {
  |         super(region, delegate);
  |         if (delegate == null) {
  |             throw new IllegalStateException("accessDelegate cannot be null and must wrap the same 'region' as passed to this constructor");
  |         }
  |         this.accessDelegate = delegate;
  |     }
  | 
  |     /**
  |      * same as TransactionalAccess.remove(key) except adds switch to NOT send replication/invalidation messages
  |      * @param key
  |      * @param localOnly
  |      * @throws org.hibernate.cache.CacheException
  |      */
  |     public void remove(CacheKey key, boolean localOnly) throws CacheException {
  |         if (localOnly) {
  |             Option option = new Option();
  |             option.setCacheModeLocal(true);
  |             getAccessDelegate().remove(key, option);
  |         } else {
  |             remove(key);
  |         }
  |     }
  | 
  |     /**
  |      * @return the accessDelegate
  |      */
  |     public DGTransactionalAccessDelegate getAccessDelegate() {
  |         return accessDelegate;
  |     }
  | }
  | 


  | /*
  |  * To change this template, choose Tools | Templates
  |  * and open the template in the editor.
  |  */
  | package com.doppelganger.framework.cache;
  | 
  | import org.hibernate.cache.CacheException;
  | import org.hibernate.cache.CacheKey;
  | import org.hibernate.cache.jbc2.BasicRegionAdapter;
  | import org.hibernate.cache.jbc2.access.TransactionalAccessDelegate;
  | import org.hibernate.cache.jbc2.util.CacheHelper;
  | import org.jboss.cache.config.Option;
  | 
  | /**
  |  *
  |  * @author nikita
  |  */
  | public class DGTransactionalAccessDelegate extends TransactionalAccessDelegate {
  | 
  |     DGTransactionalAccessDelegate(BasicRegionAdapter adapter) {
  |         super(adapter);
  |     }
  | 
  |     /**
  |      * same as remove(key), but with an Option
  |      * @param key
  |      * @param option
  |      */
  |     void remove(CacheKey key, Option option) throws CacheException {
  |         region.ensureRegionRootExists();
  |         CacheHelper.remove(cache, regionFqn, key, option);
  |     //cache.removeNode(new Fqn(region, key));
  |     //cache.evict(new Fqn(region, key));
  |     }
  | }
  | 


  | /*
  |  * To change this template, choose Tools | Templates
  |  * and open the template in the editor.
  |  */
  | package com.doppelganger.framework.cache;
  | 
  | import org.hibernate.cache.CacheDataDescription;
  | import org.hibernate.cache.CacheException;
  | import org.hibernate.cache.access.AccessType;
  | import org.hibernate.cache.access.EntityRegionAccessStrategy;
  | import org.hibernate.cache.jbc2.entity.EntityRegionImpl;
  | import org.jboss.cache.Cache;
  | 
  | /**
  |  *
  |  * @author nikita
  |  */
  | public class DGEntityRegionImpl extends EntityRegionImpl {
  | 
  |     public DGEntityRegionImpl(Cache jbcCache, String regionName, String regionPrefix, CacheDataDescription metadata) {
  |         super(jbcCache, regionName, regionPrefix, metadata);
  |     }
  | 
  |     /**
  |      * supply our own Entity TransactionalAccess impl; assume we don't need OptimisticTransactionalAccess (since we're using MVCC)
  |      * @param accessType
  |      * @return
  |      * @throws org.hibernate.cache.CacheException
  |      */
  |     @Override
  |     public EntityRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException {
  |         if (AccessType.READ_ONLY.equals(accessType)) {
  |             return new DGEntityReadOnlyAccess(this, new DGTransactionalAccessDelegate(this));
  |         }
  |         if (AccessType.TRANSACTIONAL.equals(accessType)) {
  |             return new DGEntityTransactionalAccess(this, new DGTransactionalAccessDelegate(this));
  |         }
  | 
  |         throw new CacheException("unsupported access type [" + accessType.getName() + "]");
  |     }
  | }
  | 
  | 

View the original post : http://www.jboss.org/index.html?module=bb&op=viewtopic&p=4221430#4221430

Reply to the post : http://www.jboss.org/index.html?module=bb&op=posting&mode=reply&p=4221430



More information about the jboss-user mailing list