|
From: <hib...@li...> - 2006-07-13 21:39:30
|
Author: ste...@jb...
Date: 2006-07-13 17:38:41 -0400 (Thu, 13 Jul 2006)
New Revision: 10118
Modified:
trunk/Hibernate3/src/org/hibernate/cache/OptimisticTreeCache.java
trunk/Hibernate3/src/org/hibernate/cache/StandardQueryCache.java
trunk/Hibernate3/src/org/hibernate/cache/UpdateTimestampsCache.java
Log:
corrections to OptimisticTreeCache; added some logging; formatting
Modified: trunk/Hibernate3/src/org/hibernate/cache/OptimisticTreeCache.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/cache/OptimisticTreeCache.java 2006-07-13 04:06:48 UTC (rev 10117)
+++ trunk/Hibernate3/src/org/hibernate/cache/OptimisticTreeCache.java 2006-07-13 21:38:41 UTC (rev 10118)
@@ -56,23 +56,15 @@
public void writeUpdate(Object key, Object value, Object currentVersion, Object previousVersion) {
try {
- Option option = null;
- if ( source != null ) {
- if ( source.isVersioned() ) {
- option = new Option();
- option.setDataVersion(
- new DataVersionAdapter(
- currentVersion,
- previousVersion,
- source.getVersionComparator()
- )
- );
- }
- }
+ Option option = new Option();
+ DataVersion dv = ( source != null && source.isVersioned() )
+ ? new DataVersionAdapter( currentVersion, previousVersion, source.getVersionComparator(), source.toString() )
+ : NonLockingDataVersion.INSTANCE;
+ option.setDataVersion( dv );
cache.put( new Fqn( regionFqn, key ), ITEM, value, option );
}
- catch (Exception e) {
- throw new CacheException(e);
+ catch ( Exception e ) {
+ throw new CacheException( e );
}
}
@@ -80,18 +72,15 @@
try {
Option option = new Option();
option.setFailSilently( true );
+ option.setDataVersion( NonLockingDataVersion.INSTANCE );
cache.remove( new Fqn( regionFqn, key ), "ITEM", option );
- if ( source != null ) {
- if ( source.isVersioned() ) {
- option.setDataVersion(
- new DataVersionAdapter(
- currentVersion,
- null,
- source.getVersionComparator()
- )
- );
- }
- }
+
+ option = new Option();
+ option.setFailSilently( true );
+ DataVersion dv = ( source != null && source.isVersioned() )
+ ? new DataVersionAdapter( currentVersion, currentVersion, source.getVersionComparator(), source.toString() )
+ : NonLockingDataVersion.INSTANCE;
+ option.setDataVersion( dv );
cache.put( new Fqn( regionFqn, key ), ITEM, value, option );
}
catch (Exception e) {
@@ -106,6 +95,7 @@
try {
Option option = new Option();
option.setFailSilently( true );
+// option.setDataVersion( NonLockingDataVersion.INSTANCE );
return cache.get( new Fqn( regionFqn, key ), ITEM, option );
}
catch (Exception e) {
@@ -124,7 +114,9 @@
public void update(Object key, Object value) throws CacheException {
try {
- cache.put( new Fqn( regionFqn, key ), ITEM, value );
+ Option option = new Option();
+ option.setDataVersion( NonLockingDataVersion.INSTANCE );
+ cache.put( new Fqn( regionFqn, key ), ITEM, value, option );
}
catch (Exception e) {
throw new CacheException(e);
@@ -133,9 +125,11 @@
public void put(Object key, Object value) throws CacheException {
try {
+ log.trace( "performing put() into region [" + regionName + "]" );
// do the put outside the scope of the JTA txn
Option option = new Option();
option.setFailSilently( true );
+ option.setDataVersion( NonLockingDataVersion.INSTANCE );
cache.put( new Fqn( regionFqn, key ), ITEM, value, option );
}
catch (TimeoutException te) {
@@ -149,7 +143,16 @@
public void remove(Object key) throws CacheException {
try {
- cache.remove( new Fqn( regionFqn, key ) );
+ // tree cache in optimistic mode seems to have as very difficult
+ // time with remove calls on non-existent nodes (NPEs)...
+ if ( cache.get( new Fqn( regionFqn, key ), ITEM ) != null ) {
+ Option option = new Option();
+ option.setDataVersion( NonLockingDataVersion.INSTANCE );
+ cache.remove( new Fqn( regionFqn, key ), option );
+ }
+ else {
+ log.trace( "skipping remove() call as the underlying node did not seem to exist" );
+ }
}
catch (Exception e) {
throw new CacheException(e);
@@ -158,7 +161,9 @@
public void clear() throws CacheException {
try {
- cache.remove( regionFqn );
+ Option option = new Option();
+ option.setDataVersion( NonLockingDataVersion.INSTANCE );
+ cache.remove( regionFqn, option );
}
catch (Exception e) {
throw new CacheException(e);
@@ -169,6 +174,8 @@
try {
Option option = new Option();
option.setCacheModeLocal( true );
+ option.setFailSilently( true );
+ option.setDataVersion( NonLockingDataVersion.INSTANCE );
cache.remove( regionFqn, option );
}
catch( Exception e ) {
@@ -243,19 +250,80 @@
private final Object currentVersion;
private final Object previousVersion;
private final Comparator versionComparator;
+ private final String sourceIdentifer;
- public DataVersionAdapter(Object currentVersion, Object previousVersion, Comparator versionComparator) {
+ public DataVersionAdapter(Object currentVersion, Object previousVersion, Comparator versionComparator, String sourceIdentifer) {
this.currentVersion = currentVersion;
this.previousVersion = previousVersion;
this.versionComparator = versionComparator;
+ this.sourceIdentifer = sourceIdentifer;
+ log.trace( "created " + this );
}
+ /**
+ * newerThan() call is dispatched against the DataVersion currently
+ * associated with the node; the passed dataVersion param is the
+ * DataVersion associated with the data we are trying to put into
+ * the node.
+ * <p/>
+ * we are expected to return true in the case where we (the current
+ * node DataVersion) are newer that then incoming value. Returning
+ * true here essentially means that a optimistic lock failure has
+ * occured (because conversely, the value we are trying to put into
+ * the node is "older than" the value already there...)
+ */
public boolean newerThan(DataVersion dataVersion) {
- if ( previousVersion == null ) {
- log.warn( "Unexpected optimistic lock check on inserted data" );
+ log.trace( "checking [" + this + "] against [" + dataVersion + "]" );
+ if ( dataVersion instanceof CircumventChecksDataVersion ) {
+ log.trace( "skipping lock checks..." );
+ return false;
}
- Object other = ( ( DataVersionAdapter ) dataVersion ).currentVersion;
- return versionComparator.compare( previousVersion, other ) > 1;
+ else if ( dataVersion instanceof NonLockingDataVersion ) {
+ // can happen because of the multiple ways Cache.remove()
+ // can be invoked :(
+ log.trace( "skipping lock checks..." );
+ return false;
+ }
+ DataVersionAdapter other = ( DataVersionAdapter ) dataVersion;
+ if ( other.previousVersion == null ) {
+ log.warn( "Unexpected optimistic lock check on inserting data" );
+ // work around the "feature" where tree cache is validating the
+ // inserted node during the next transaction. no idea...
+ if ( this == dataVersion ) {
+ log.trace( "skipping lock checks due to same DV instance" );
+ return false;
+ }
+ }
+ return versionComparator.compare( currentVersion, other.previousVersion ) >= 1;
}
+
+ public String toString() {
+ return super.toString() + " [current=" + currentVersion + ", previous=" + previousVersion + ", src=" + sourceIdentifer + "]";
+ }
}
+
+ /**
+ * Used in regions where no locking should ever occur. This includes query-caches,
+ * update-timestamps caches, collection caches, and entity caches where the entity
+ * is not versioned.
+ */
+ public static class NonLockingDataVersion implements DataVersion {
+ public static final DataVersion INSTANCE = new NonLockingDataVersion();
+ public boolean newerThan(DataVersion dataVersion) {
+ log.trace( "non locking lock check...");
+ return false;
+ }
+ }
+
+ /**
+ * Used to signal to a DataVersionAdapter to simply not perform any checks. This
+ * is currently needed for proper handling of remove() calls for entity cache regions
+ * (we do not know the version info...).
+ */
+ public static class CircumventChecksDataVersion implements DataVersion {
+ public static final DataVersion INSTANCE = new CircumventChecksDataVersion();
+ public boolean newerThan(DataVersion dataVersion) {
+ throw new CacheException( "optimistic locking checks should never happen on CircumventChecksDataVersion" );
+ }
+ }
}
Modified: trunk/Hibernate3/src/org/hibernate/cache/StandardQueryCache.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/cache/StandardQueryCache.java 2006-07-13 04:06:48 UTC (rev 10117)
+++ trunk/Hibernate3/src/org/hibernate/cache/StandardQueryCache.java 2006-07-13 21:38:41 UTC (rev 10118)
@@ -27,7 +27,7 @@
*/
public class StandardQueryCache implements QueryCache {
- private static final Log log = LogFactory.getLog(StandardQueryCache.class);
+ private static final Log log = LogFactory.getLog( StandardQueryCache.class );
private Cache queryCache;
private UpdateTimestampsCache updateTimestampsCache;
@@ -41,34 +41,40 @@
final Settings settings,
final Properties props,
final UpdateTimestampsCache updateTimestampsCache,
- String regionName)
- throws HibernateException {
-
- if (regionName==null) regionName = StandardQueryCache.class.getName();
+ String regionName) throws HibernateException {
+ if ( regionName == null ) {
+ regionName = StandardQueryCache.class.getName();
+ }
String prefix = settings.getCacheRegionPrefix();
- if (prefix!=null) regionName = prefix + '.' + regionName;
-
- log.info("starting query cache at region: " + regionName);
-
+ if ( prefix != null ) {
+ regionName = prefix + '.' + regionName;
+ }
+ log.info( "starting query cache at region: " + regionName );
+
this.queryCache = settings.getCacheProvider().buildCache(regionName, props);
this.updateTimestampsCache = updateTimestampsCache;
this.regionName = regionName;
}
- public boolean put(QueryKey key, Type[] returnTypes, List result, boolean isNaturalKeyLookup, SessionImplementor session)
- throws HibernateException {
+ public boolean put(
+ QueryKey key,
+ Type[] returnTypes,
+ List result,
+ boolean isNaturalKeyLookup,
+ SessionImplementor session) throws HibernateException {
if ( isNaturalKeyLookup && result.size()==0 ) {
return false;
}
else {
-
+ Long ts = new Long( session.getTimestamp() );
+
if ( log.isDebugEnabled() ) {
- log.debug("caching query results in region: " + regionName);
+ log.debug( "caching query results in region: " + regionName + "; timestamp=" + ts );
}
List cacheable = new ArrayList( result.size()+1 );
- cacheable.add( new Long( session.getTimestamp() ) );
+ cacheable.add( ts );
for ( int i=0; i<result.size(); i++ ) {
if ( returnTypes.length==1 ) {
cacheable.add( returnTypes[0].disassemble( result.get(i), session, null ) );
@@ -86,25 +92,28 @@
}
- public List get(QueryKey key, Type[] returnTypes, boolean isNaturalKeyLookup, Set spaces, SessionImplementor session)
- throws HibernateException {
-
+ public List get(
+ QueryKey key,
+ Type[] returnTypes,
+ boolean isNaturalKeyLookup,
+ Set spaces,
+ SessionImplementor session) throws HibernateException {
if ( log.isDebugEnabled() ) {
log.debug("checking cached query results in region: " + regionName);
}
-
+
List cacheable = (List) queryCache.get(key);
if (cacheable==null) {
log.debug("query results were not found in cache");
return null;
}
-
+
Long timestamp = (Long) cacheable.get(0);
if ( !isNaturalKeyLookup && !isUpToDate(spaces, timestamp) ) {
log.debug("cached query results were not up to date");
return null;
}
-
+
log.debug("returning cached query results");
for ( int i=1; i<cacheable.size(); i++ ) {
if ( returnTypes.length==1 ) {
Modified: trunk/Hibernate3/src/org/hibernate/cache/UpdateTimestampsCache.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/cache/UpdateTimestampsCache.java 2006-07-13 04:06:48 UTC (rev 10117)
+++ trunk/Hibernate3/src/org/hibernate/cache/UpdateTimestampsCache.java 2006-07-13 21:38:41 UTC (rev 10118)
@@ -18,37 +18,37 @@
* to a higher value than the timeouts of any of the query caches. In fact, we
* recommend that the the underlying cache not be configured for expiry at all.
* Note, in particular, that an LRU cache expiry policy is never appropriate.
- * @author Gavin King, Mikheil Kapanadze
+ *
+ * @author Gavin King
+ * @author Mikheil Kapanadze
*/
public class UpdateTimestampsCache {
+ public static final String REGION_NAME = UpdateTimestampsCache.class.getName();
+
private static final Log log = LogFactory.getLog(UpdateTimestampsCache.class);
private Cache updateTimestamps;
private final String regionName;
- public static final String REGION_NAME = UpdateTimestampsCache.class.getName();
-
public void clear() throws CacheException {
updateTimestamps.clear();
}
- public UpdateTimestampsCache(Settings settings, Properties props)
- throws HibernateException {
+ public UpdateTimestampsCache(Settings settings, Properties props) throws HibernateException {
String prefix = settings.getCacheRegionPrefix();
-
- regionName = prefix==null ?
- REGION_NAME :
- prefix + '.' + REGION_NAME;
- log.info("starting update timestamps cache at region: " + regionName);
- this.updateTimestamps = settings.getCacheProvider().buildCache(regionName, props);
+ regionName = prefix == null ? REGION_NAME : prefix + '.' + REGION_NAME;
+ log.info( "starting update timestamps cache at region: " + regionName );
+ this.updateTimestamps = settings.getCacheProvider().buildCache( regionName, props );
}
public synchronized void preinvalidate(Serializable[] spaces) throws CacheException {
//TODO: to handle concurrent writes correctly, this should return a Lock to the client
Long ts = new Long( updateTimestamps.nextTimestamp() + updateTimestamps.getTimeout() );
for ( int i=0; i<spaces.length; i++ ) {
- if ( log.isDebugEnabled() ) log.debug("Pre-invalidating space [" + spaces[i] + "]");
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Pre-invalidating space [" + spaces[i] + "]" );
+ }
//put() has nowait semantics, is this really appropriate?
//note that it needs to be async replication, never local or sync
updateTimestamps.put( spaces[i], ts );
@@ -61,7 +61,9 @@
Long ts = new Long( updateTimestamps.nextTimestamp() );
//TODO: if lock.getTimestamp().equals(ts)
for ( int i=0; i<spaces.length; i++ ) {
- if ( log.isDebugEnabled() ) log.debug("Invalidating space [" + spaces[i] + "], timestamp: " + ts);
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Invalidating space [" + spaces[i] + "], timestamp: " + ts);
+ }
//put() has nowait semantics, is this really appropriate?
//note that it needs to be async replication, never local or sync
updateTimestamps.put( spaces[i], ts );
@@ -83,7 +85,8 @@
if ( log.isDebugEnabled() ) {
log.debug("[" + space + "] last update timestamp: " + lastUpdate + ", result set timestamp: " + timestamp );
}
- if ( lastUpdate.longValue() >= timestamp.longValue() ) return false;
+ return lastUpdate.longValue() < timestamp.longValue();
+// if ( lastUpdate.longValue() >= timestamp.longValue() ) return false;
}
}
return true;
|