Update handling of deletion of the region root node

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@14263 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Brian Stansberry 2007-12-23 14:59:07 +00:00
parent 8277156f00
commit 9352a7a7d7
9 changed files with 125 additions and 38 deletions

View File

@ -40,11 +40,16 @@ import org.jboss.cache.config.Option;
import org.jboss.cache.config.Configuration.NodeLockingScheme;
import org.jboss.cache.notifications.annotation.CacheListener;
import org.jboss.cache.notifications.annotation.NodeCreated;
import org.jboss.cache.notifications.annotation.NodeRemoved;
import org.jboss.cache.notifications.event.NodeCreatedEvent;
import org.jboss.cache.notifications.event.NodeRemovedEvent;
import org.jboss.cache.optimistic.DataVersion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.Region;
import org.hibernate.cache.jbc2.builder.JndiMultiplexingCacheInstanceManager;
import org.hibernate.cache.jbc2.util.CacheHelper;
import org.hibernate.cache.jbc2.util.NonLockingDataVersion;
@ -56,16 +61,20 @@ import org.hibernate.cache.jbc2.util.NonLockingDataVersion;
*/
@CacheListener
public abstract class BasicRegionAdapter implements Region {
public static final String ITEM = CacheHelper.ITEM;
protected final Cache jbcCache;
protected final String regionName;
protected final Fqn regionFqn;
protected Node regionRoot;
protected final boolean optimistic;
protected final TransactionManager transactionManager;
protected final Logger log;
protected final Object regionRootMutex = new Object();
protected SetResidentListener listener;
protected RegionRootListener listener;
public BasicRegionAdapter(Cache jbcCache, String regionName, String regionPrefix) {
this.jbcCache = jbcCache;
@ -73,6 +82,7 @@ public abstract class BasicRegionAdapter implements Region {
this.regionName = regionName;
this.regionFqn = createRegionFqn(regionName, regionPrefix);
optimistic = jbcCache.getConfiguration().getNodeLockingScheme() == NodeLockingScheme.OPTIMISTIC;
log = LoggerFactory.getLogger(getClass());
activateLocalClusterNode();
}
@ -97,33 +107,50 @@ public abstract class BasicRegionAdapter implements Region {
// and then need to re-add it. In that case, the fact
// that it is resident will not replicate, so use a listener
// to set it as resident
if (CacheHelper.isClusteredReplication(cfg.getCacheMode())) {
listener = new SetResidentListener();
if (CacheHelper.isClusteredReplication(cfg.getCacheMode())
|| CacheHelper.isClusteredInvalidation(cfg.getCacheMode())) {
listener = new RegionRootListener();
jbcCache.addCacheListener(listener);
}
// Make sure the root node for the region exists and
// has a DataVersion that never complains
Node regionRoot = jbcCache.getRoot().getChild( regionFqn );
if (regionRoot == null) {
// Establish the region root node with a non-locking data version
DataVersion version = optimistic ? NonLockingDataVersion.INSTANCE : null;
regionRoot = CacheHelper.addNode(jbcCache, regionFqn, true, true, version);
}
else if (optimistic && regionRoot instanceof NodeSPI) {
// FIXME Hacky workaround to JBCACHE-1202
if ( !( ( ( NodeSPI ) regionRoot ).getVersion() instanceof NonLockingDataVersion ) ) {
((NodeSPI) regionRoot).setVersion(NonLockingDataVersion.INSTANCE);
}
}
// Never evict this node
regionRoot.setResident(true);
establishRegionRootNode();
}
catch (Exception e) {
throw new CacheException(e.getMessage(), e);
}
}
private void establishRegionRootNode()
{
synchronized (regionRootMutex) {
if (regionRoot != null && regionRoot.isValid())
return;
// Don't hold a transactional lock for this
Transaction tx = suspend();
try {
// Make sure the root node for the region exists and
// has a DataVersion that never complains
regionRoot = jbcCache.getRoot().getChild( regionFqn );
if (regionRoot == null) {
// Establish the region root node with a non-locking data version
DataVersion version = optimistic ? NonLockingDataVersion.INSTANCE : null;
regionRoot = CacheHelper.addNode(jbcCache, regionFqn, true, true, version);
}
else if (optimistic && regionRoot instanceof NodeSPI) {
// FIXME Hacky workaround to JBCACHE-1202
if ( !( ( ( NodeSPI ) regionRoot ).getVersion() instanceof NonLockingDataVersion ) ) {
((NodeSPI) regionRoot).setVersion(NonLockingDataVersion.INSTANCE);
}
}
// Never evict this node
regionRoot.setResident(true);
}
finally {
resume(tx);
}
}
}
public String getName() {
return regionName;
}
@ -135,12 +162,29 @@ public abstract class BasicRegionAdapter implements Region {
public Fqn getRegionFqn() {
return regionFqn;
}
/**
* If the cache is configured for optimistic locking, checks for the
* validity of the root cache node for this region,
* creating a new one if it does not exist or is invalid. Suspends any
* transaction while doing this to ensure no transactional locks are held
* on the region root.
*
* This is only needed for optimistic locking, as with optimistic the
* region root node has a special version that must be established.
*
* TODO remove this once JBCACHE-1250 is resolved.
*/
public void ensureRegionRootExists() {
if (optimistic && (regionRoot == null || !regionRoot.isValid()))
establishRegionRootNode();
}
public void destroy() throws CacheException {
try {
// NOTE : this is being used from the process of shutting down a
// SessionFactory. Specific things to consider:
// (1) this clearing of the region should not propogate to
// (1) this clearing of the region should not propagate to
// other nodes on the cluster (if any); this is the
// cache-mode-local option bit...
// (2) really just trying a best effort to cleanup after
@ -321,11 +365,12 @@ public abstract class BasicRegionAdapter implements Region {
}
@CacheListener
public class SetResidentListener {
public class RegionRootListener {
@NodeCreated
public void nodeCreated(NodeCreatedEvent event) {
if (!event.isPre() && event.getFqn().equals(getRegionFqn())) {
log.debug("Node created for " + getRegionFqn());
Node regionRoot = jbcCache.getRoot().getChild(getRegionFqn());
regionRoot.setResident(true);
}

View File

@ -27,11 +27,11 @@ import org.hibernate.cache.CacheDataDescription;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.access.CollectionRegionAccessStrategy;
import org.hibernate.cache.access.EntityRegionAccessStrategy;
import org.hibernate.cache.jbc2.BasicRegionAdapter;
import org.hibernate.cache.jbc2.TransactionalDataRegionAdapter;
import org.hibernate.cache.jbc2.util.CacheHelper;
import org.hibernate.cache.jbc2.util.DataVersionAdapter;
import org.hibernate.cache.jbc2.util.NonLockingDataVersion;
import org.jboss.cache.Cache;
import org.jboss.cache.Fqn;
import org.jboss.cache.config.Option;
import org.jboss.cache.optimistic.DataVersion;
@ -51,9 +51,9 @@ public class OptimisticTransactionalAccessDelegate extends TransactionalAccessDe
protected final CacheDataDescription dataDescription;
public OptimisticTransactionalAccessDelegate(Cache cache, Fqn regionFqn, CacheDataDescription dataDescription) {
super(cache, regionFqn);
this.dataDescription = dataDescription;
public OptimisticTransactionalAccessDelegate(TransactionalDataRegionAdapter region) {
super(region);
this.dataDescription = region.getCacheDataDescription();
}
/**
@ -63,6 +63,8 @@ public class OptimisticTransactionalAccessDelegate extends TransactionalAccessDe
*/
@Override
public void evict(Object key) throws CacheException {
region.ensureRegionRootExists();
Option opt = NonLockingDataVersion.getInvocationOption();
CacheHelper.remove(cache, regionFqn, key, opt);
@ -76,6 +78,18 @@ public class OptimisticTransactionalAccessDelegate extends TransactionalAccessDe
public void evictAll() throws CacheException {
evictOrRemoveAll();
}
/**
* Overrides the {@link TransactionalAccessDelegate#get(Object, long) superclass}
* by {@link BasicRegionAdapter#ensureRegionRootExists() ensuring the root
* node for the region exists} before making the call.
*/
@Override
public Object get(Object key, long txTimestamp) throws CacheException
{
region.ensureRegionRootExists();
return super.get(key, txTimestamp);
}
/**
@ -85,6 +99,8 @@ public class OptimisticTransactionalAccessDelegate extends TransactionalAccessDe
*/
@Override
public boolean insert(Object key, Object value, Object version) throws CacheException {
region.ensureRegionRootExists();
Option opt = getDataVersionOption(version, null);
CacheHelper.put(cache, regionFqn, key, value, opt);
@ -94,6 +110,8 @@ public class OptimisticTransactionalAccessDelegate extends TransactionalAccessDe
@Override
public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride)
throws CacheException {
region.ensureRegionRootExists();
// We ignore minimalPutOverride. JBossCache putForExternalRead is
// already about as minimal as we can get; it will promptly return
@ -104,6 +122,8 @@ public class OptimisticTransactionalAccessDelegate extends TransactionalAccessDe
@Override
public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version) throws CacheException {
region.ensureRegionRootExists();
Option opt = getDataVersionOption(version, version);
return CacheHelper.putForExternalRead(cache, regionFqn, key, value, opt);
@ -111,6 +131,8 @@ public class OptimisticTransactionalAccessDelegate extends TransactionalAccessDe
@Override
public void remove(Object key) throws CacheException {
region.ensureRegionRootExists();
Option opt = NonLockingDataVersion.getInvocationOption();
CacheHelper.remove(cache, regionFqn, key, opt);
@ -125,6 +147,8 @@ public class OptimisticTransactionalAccessDelegate extends TransactionalAccessDe
@Override
public boolean update(Object key, Object value, Object currentVersion, Object previousVersion)
throws CacheException {
region.ensureRegionRootExists();
Option opt = getDataVersionOption(currentVersion, previousVersion);
CacheHelper.put(cache, regionFqn, key, value, opt);
@ -132,6 +156,7 @@ public class OptimisticTransactionalAccessDelegate extends TransactionalAccessDe
}
private Option getDataVersionOption(Object currentVersion, Object previousVersion) {
DataVersion dv = (dataDescription != null && dataDescription.isVersioned()) ? new DataVersionAdapter(
currentVersion, previousVersion, dataDescription.getVersionComparator(), dataDescription.toString())
: NonLockingDataVersion.INSTANCE;
@ -141,6 +166,7 @@ public class OptimisticTransactionalAccessDelegate extends TransactionalAccessDe
}
private void evictOrRemoveAll() {
Option opt = NonLockingDataVersion.getInvocationOption();
CacheHelper.removeAll(cache, regionFqn, opt);

View File

@ -27,6 +27,7 @@ import org.hibernate.cache.CacheException;
import org.hibernate.cache.access.CollectionRegionAccessStrategy;
import org.hibernate.cache.access.EntityRegionAccessStrategy;
import org.hibernate.cache.access.SoftLock;
import org.hibernate.cache.jbc2.BasicRegionAdapter;
import org.hibernate.cache.jbc2.util.CacheHelper;
import org.jboss.cache.Cache;
import org.jboss.cache.Fqn;
@ -46,10 +47,12 @@ public class TransactionalAccessDelegate {
protected final Cache cache;
protected final Fqn regionFqn;
protected final BasicRegionAdapter region;
public TransactionalAccessDelegate(Cache cache, Fqn regionFqn) {
this.cache = cache;
this.regionFqn = regionFqn;
public TransactionalAccessDelegate(BasicRegionAdapter adapter) {
this.region = adapter;
this.cache = adapter.getCacheInstance();
this.regionFqn = adapter.getRegionFqn();
}
public Object get(Object key, long txTimestamp) throws CacheException {

View File

@ -42,8 +42,7 @@ public class OptimisticTransactionalAccess extends TransactionalAccess {
public OptimisticTransactionalAccess(CollectionRegionImpl region) {
// We use a different delegate than the non-optimistic superclass default
super(region, new OptimisticTransactionalAccessDelegate(region.getCacheInstance(), region.getRegionFqn(),
region.getCacheDataDescription()));
super(region, new OptimisticTransactionalAccessDelegate(region));
}
}

View File

@ -52,7 +52,7 @@ public class TransactionalAccess implements CollectionRegionAccessStrategy {
* @param region the region to which this provides access
*/
public TransactionalAccess(CollectionRegionImpl region) {
this(region, new TransactionalAccessDelegate(region.getCacheInstance(), region.getRegionFqn()));
this(region, new TransactionalAccessDelegate(region));
}
/**

View File

@ -41,7 +41,6 @@ public class OptimisticTransactionalAccess extends TransactionalAccess {
* @param region The region\ to which this is providing access
*/
public OptimisticTransactionalAccess(EntityRegionImpl region) {
super(region, new OptimisticTransactionalAccessDelegate(region.getCacheInstance(), region.getRegionFqn(),
region.getCacheDataDescription()));
super(region, new OptimisticTransactionalAccessDelegate(region));
}
}

View File

@ -46,7 +46,7 @@ public class TransactionalAccess implements EntityRegionAccessStrategy {
private final TransactionalAccessDelegate delegate;
public TransactionalAccess(EntityRegionImpl region) {
this(region, new TransactionalAccessDelegate(region.getCacheInstance(), region.getRegionFqn()));
this(region, new TransactionalAccessDelegate(region));
}
protected TransactionalAccess(EntityRegionImpl region, TransactionalAccessDelegate delegate) {

View File

@ -75,6 +75,9 @@ public class QueryResultsRegionImpl extends TransactionalDataRegionAdapter imple
}
public void evict(Object key) throws CacheException {
ensureRegionRootExists();
Option opt = getNonLockingDataVersionOption(false);
if (localOnly)
opt.setCacheModeLocal(true);
@ -91,6 +94,8 @@ public class QueryResultsRegionImpl extends TransactionalDataRegionAdapter imple
}
public Object get(Object key) throws CacheException {
ensureRegionRootExists();
// Don't hold the JBC node lock throughout the tx, as that
// prevents updates
@ -102,6 +107,8 @@ public class QueryResultsRegionImpl extends TransactionalDataRegionAdapter imple
}
public void put(Object key, Object value) throws CacheException {
ensureRegionRootExists();
// Here we don't want to suspend the tx. If we do:
// 1) We might be caching query results that reflect uncommitted
@ -110,9 +117,9 @@ public class QueryResultsRegionImpl extends TransactionalDataRegionAdapter imple
// 2) No tx == immediate replication. More overhead, plus we
// spread issue #1 above around the cluster
// Add a zero (or quite low) timeout option so we don't block
// Add a zero (or quite low) timeout option so we don't block.
// Ignore any TimeoutException. Basically we forego caching the
// query result in order to avoid blocking for concurrent reads.
// query result in order to avoid blocking.
// Reads are done with suspended tx, so they should not hold the
// lock for long. Not caching the query result is OK, since
// any subsequent read will just see the old result with its

View File

@ -85,6 +85,9 @@ public class TimestampsRegionImpl extends TransactionalDataRegionAdapter impleme
}
public void evict(Object key) throws CacheException {
ensureRegionRootExists();
// TODO Is this a valid operation on a timestamps cache?
Option opt = getNonLockingDataVersionOption(true);
CacheHelper.removeNode(getCacheInstance(), getRegionFqn(), key, opt);
@ -102,6 +105,9 @@ public class TimestampsRegionImpl extends TransactionalDataRegionAdapter impleme
Object value = localCache.get(key);
if (value == null) {
ensureRegionRootExists();
value = suspendAndGet(key, null, false);
if (value != null)
localCache.put(key, value);
@ -110,6 +116,8 @@ public class TimestampsRegionImpl extends TransactionalDataRegionAdapter impleme
}
public void put(Object key, Object value) throws CacheException {
ensureRegionRootExists();
// Don't hold the JBC node lock throughout the tx, as that
// prevents reads and other updates