Get rid of "never" go backwards enforcement, as preinvalidate()/invalidate() sequence is 99.99% likely to go backwards

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@14123 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Brian Stansberry 2007-10-19 19:29:57 +00:00
parent bd75e6db03
commit c0f3881bae
1 changed files with 30 additions and 88 deletions

View File

@ -38,14 +38,12 @@ import org.jboss.cache.notifications.event.NodeRemovedEvent;
/**
* Defines the behavior of the timestamps cache region for JBossCache 2.x.
* <p>
* Maintains a local (authoritative) cache of timestamps along with the
* distributed cache held in JBoss Cache. Listens for changes in the distributed
* cache and updates the local cache accordingly. Ensures that any changes in
* the local cache represent an increase in the timestamp. This approach allows
* timestamp changes to be replicated asynchronously by JBoss Cache while still
* preventing backward changes in timestamps.
* </p>
*
* TODO Need to define a way to ensure asynchronous replication events
* do not result in timestamps moving backward, while dealing with the fact
* that the normal sequence of UpdateTimestampsCache.preinvalidate() then
* UpdateTimestampsCache.invalidate() will result in 2 calls to put() with
* the latter call having an earlier timestamp.
*
* @author Brian Stansberry
* @version $Revision$
@ -54,9 +52,9 @@ import org.jboss.cache.notifications.event.NodeRemovedEvent;
public class TimestampsRegionImpl extends TransactionalDataRegionAdapter implements TimestampsRegion {
public static final String TYPE = "TS";
private final Map localCache = new ConcurrentHashMap();
private Map localCache = new ConcurrentHashMap();
/**
* Create a new TimestampsRegionImpl.
*
@ -81,14 +79,12 @@ public class TimestampsRegionImpl extends TransactionalDataRegionAdapter impleme
public void evict(Object key) throws CacheException {
// TODO Is this a valid operation on a timestamps cache?
localCache.remove(key);
Option opt = getNonLockingDataVersionOption(true);
CacheHelper.removeNode(getCacheInstance(), getRegionFqn(), key, opt);
}
public void evictAll() throws CacheException {
// TODO Is this a valid operation on a timestamps cache?
localCache.clear();
Option opt = getNonLockingDataVersionOption(true);
CacheHelper.removeAll(getCacheInstance(), getRegionFqn(), opt);
// Restore the region root node
@ -97,50 +93,35 @@ public class TimestampsRegionImpl extends TransactionalDataRegionAdapter impleme
public Object get(Object key) throws CacheException {
Object timestamp = localCache.get(key);
if (timestamp == null) {
// Check the cluster-wide cache
// Don't hold the cache node lock throughout the tx, as that
// prevents updates
timestamp = suspendAndGet(key, null, false);
updateLocalCache(key, timestamp);
Object value = localCache.get(key);
if (value == null) {
value = suspendAndGet(key, null, false);
if (value != null)
localCache.put(key, value);
}
return timestamp;
return value;
}
public void put(Object key, Object value) throws CacheException {
// Immediately update the local cache
boolean incremented = updateLocalCache(key, value);
if (incremented) {
// Now the cluster-wide cache
// TODO there's a race here where 2 threads can get through
// updateLocalCache() in proper sequence but then the earlier
// one updates JBC *later*. This should only affect newly
// joined nodes who populate their initial localCache from JBC.
// Don't hold the JBC node lock throughout the tx, as that
// prevents reads and other updates
Transaction tx = suspend();
try {
// TODO Why not use the timestamp in a DataVersion?
Option opt = getNonLockingDataVersionOption(false);
// We ensure ASYNC semantics (JBCACHE-1175)
opt.setForceAsynchronous(true);
CacheHelper.put(getCacheInstance(), getRegionFqn(), key, value, opt);
} catch (Exception e) {
throw new CacheException(e);
} finally {
resume(tx);
}
// Don't hold the JBC node lock throughout the tx, as that
// prevents reads and other updates
Transaction tx = suspend();
try {
// TODO Why not use the timestamp in a DataVersion?
Option opt = getNonLockingDataVersionOption(false);
// We ensure ASYNC semantics (JBCACHE-1175)
opt.setForceAsynchronous(true);
CacheHelper.put(getCacheInstance(), getRegionFqn(), key, value, opt);
} catch (Exception e) {
throw new CacheException(e);
} finally {
resume(tx);
}
}
@Override
public void destroy() throws CacheException {
localCache.clear();
getCacheInstance().removeCacheListener(this);
super.destroy();
@ -153,14 +134,14 @@ public class TimestampsRegionImpl extends TransactionalDataRegionAdapter impleme
*/
@NodeModified
public void nodeModified(NodeModifiedEvent event) {
if (event.isOriginLocal() || event.isPre())
if (event.isPre())
return;
Fqn fqn = event.getFqn();
Fqn regFqn = getRegionFqn();
if (fqn.size() == regFqn.size() + 1 && fqn.isChildOf(regFqn)) {
Object key = fqn.get(regFqn.size());
updateLocalCache(key, event.getData().get(ITEM));
localCache.put(key, event.getData().get(ITEM));
}
}
@ -171,7 +152,7 @@ public class TimestampsRegionImpl extends TransactionalDataRegionAdapter impleme
*/
@NodeRemoved
public void nodeRemoved(NodeRemovedEvent event) {
if (event.isOriginLocal() || event.isPre())
if (event.isPre())
return;
Fqn fqn = event.getFqn();
@ -194,43 +175,4 @@ public class TimestampsRegionImpl extends TransactionalDataRegionAdapter impleme
get(key);
}
}
/**
* Updates the local cache, ensuring that the new value represents a higher
* value than the old (i.e. timestamp never goes back in time).
*
* @param key
* @param value
*/
private boolean updateLocalCache(Object key, Object value) {
if (value == null)
return false;
boolean increase = true;
long newVal = 0;
try {
newVal = ((Long) value).longValue();
Long oldVal = (Long) localCache.get(key);
increase = oldVal == null || newVal > oldVal.longValue();
if (increase) {
oldVal = (Long) localCache.put(key, value);
// Double check that it was an increase
if (oldVal != null && oldVal.longValue() > newVal) {
// Nope; Restore the old value
updateLocalCache(key, oldVal);
increase = false;
}
}
} catch (ClassCastException cce) {
// TODO -- this is stupid; look into changing TimestampsRegion API
// not using Long; just store it
localCache.put(key, value);
}
return increase;
}
}