Author: manik.surtani(a)jboss.com
Date: 2007-09-18 12:43:17 -0400 (Tue, 18 Sep 2007)
New Revision: 4484
Added:
core/trunk/src/test/java/org/jboss/cache/invalidation/VersionInconsistencyTest.java
Modified:
core/trunk/src/main/java/org/jboss/cache/CacheImpl.java
core/trunk/src/main/java/org/jboss/cache/interceptors/InvalidationInterceptor.java
core/trunk/src/main/java/org/jboss/cache/marshall/MethodDeclarations.java
core/trunk/src/main/java/org/jboss/cache/util/Util.java
Log:
JBCACHE-1155
Modified: core/trunk/src/main/java/org/jboss/cache/CacheImpl.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/CacheImpl.java 2007-09-18 03:43:06 UTC (rev
4483)
+++ core/trunk/src/main/java/org/jboss/cache/CacheImpl.java 2007-09-18 16:43:17 UTC (rev
4484)
@@ -2769,26 +2769,64 @@
if (!exists(fqn))
return true;// node does not exist
- boolean create_undo_ops = false;
- boolean sendNodeEvent = false;
- boolean eviction = true;
if (log.isTraceEnabled())
{
log.trace("_evict(" + fqn + ", " + version +
")");
}
+
if (hasChild(fqn))
{
- _removeData(null, fqn, create_undo_ops, sendNodeEvent, eviction, version);
+ _removeData(null, fqn, false, false, true, version);
return false;
}
else
{
- _remove(null, fqn, create_undo_ops, sendNodeEvent, eviction, version);
+ _remove(null, fqn, false, false, true, version);
return true;
}
}
/**
+ * Very much like an evict(), except that regardless of whether there is a child
present, this call will never
+ * remove the node from memory - just remove its contents.
+ *
+ * Also, potentially throws a cache exception if data versioning is used and the node
in memory has a newer data
+ * version than what is passed in.
+ *
+ * Finally, the data version of the in-memory node is updated to the version being
evicted to prevent versions
+ * going out of sync.
+ *
+ * @param fqn
+ * @param versionToInvalidate
+ */
+ public void invalidate(Fqn fqn, DataVersion versionToInvalidate)
+ {
+ Node node = get(fqn); // force interceptor chain, load if necessary from cache
loader.
+
+ if (node != null)
+ {
+ _removeData(null, fqn, false, false, true, versionToInvalidate);
+ if (versionToInvalidate != null)
+ {
+ NodeSPI n = peek(fqn, false);
+ n.setVersion(versionToInvalidate);
+ }
+ }
+ else
+ {
+ // if pessimistic locking, just return.
+ if (!configuration.isNodeLockingOptimistic()) return;
+ // create the node we need.
+ Map<K, V> m = Collections.emptyMap();
+ getInvocationContext().getOptionOverrides().setCacheModeLocal(true);
+ put(fqn, m);
+ getInvocationContext().getOptionOverrides().setCacheModeLocal(false);
+ NodeSPI nodeSPI = (NodeSPI) root.getChild(fqn);
+ nodeSPI.setVersion(versionToInvalidate);
+ }
+ }
+
+ /**
* Evicts a key/value pair from a node's attributes. Note that this is
<em>local</em>, will not be replicated.
* @param fqn
* @param key
Modified:
core/trunk/src/main/java/org/jboss/cache/interceptors/InvalidationInterceptor.java
===================================================================
---
core/trunk/src/main/java/org/jboss/cache/interceptors/InvalidationInterceptor.java 2007-09-18
03:43:06 UTC (rev 4483)
+++
core/trunk/src/main/java/org/jboss/cache/interceptors/InvalidationInterceptor.java 2007-09-18
16:43:17 UTC (rev 4484)
@@ -191,9 +191,12 @@
m_invalidations++;
// only propagate version details if we're using explicit versioning.
+ /*
MethodCall call = workspace != null && !workspace.isVersioningImplicit() ?
MethodCallFactory.create(MethodDeclarations.evictVersionedNodeMethodLocal,
fqn, workspace.getNode(fqn).getVersion()) :
MethodCallFactory.create(MethodDeclarations.evictNodeMethodLocal, fqn);
+ */
+ MethodCall call =
MethodCallFactory.create(MethodDeclarations.invalidateMethodLocal, fqn, (workspace == null
? null : workspace.getNode(fqn).getVersion()));
if (log.isDebugEnabled()) log.debug("Cache [" + cache.getLocalAddress() +
"] replicating " + call);
// voila, invalidated!
Modified: core/trunk/src/main/java/org/jboss/cache/marshall/MethodDeclarations.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/marshall/MethodDeclarations.java 2007-09-18
03:43:06 UTC (rev 4483)
+++ core/trunk/src/main/java/org/jboss/cache/marshall/MethodDeclarations.java 2007-09-18
16:43:17 UTC (rev 4484)
@@ -133,6 +133,8 @@
public static final Method removeDataVersionedMethodLocal;
+ public static final Method invalidateMethodLocal;
+
//not all of these are used for RPC - trim accordingly.
public static final int putDataMethodLocal_id = 1;
@@ -223,7 +225,9 @@
public static final int putForExternalReadVersionedMethodLocal_id = 46;
+ public static final int invalidateMethodLocal_id = 47;
+
static
{
try
@@ -289,6 +293,8 @@
removeKeyVersionedMethodLocal =
CacheImpl.class.getDeclaredMethod("_remove", GlobalTransaction.class, Fqn.class,
Object.class, boolean.class, DataVersion.class);
removeDataVersionedMethodLocal =
CacheImpl.class.getDeclaredMethod("_removeData", GlobalTransaction.class,
Fqn.class, boolean.class, DataVersion.class);
+ invalidateMethodLocal =
CacheImpl.class.getDeclaredMethod("invalidate", Fqn.class, DataVersion.class);
+
}
catch (NoSuchMethodException e)
{
@@ -342,6 +348,8 @@
methods.put(putForExternalReadVersionedMethodLocal_id,
putForExternalReadVersionedMethodLocal);
methods.put(putForExternalReadMethodLocal_id, putForExternalReadMethodLocal);
+ methods.put(invalidateMethodLocal_id, invalidateMethodLocal);
+
for (Integer id : methods.keySet())
{
methodIds.put(methods.get(id), id);
Modified: core/trunk/src/main/java/org/jboss/cache/util/Util.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/util/Util.java 2007-09-18 03:43:06 UTC (rev
4483)
+++ core/trunk/src/main/java/org/jboss/cache/util/Util.java 2007-09-18 16:43:17 UTC (rev
4484)
@@ -60,9 +60,9 @@
}
/**
- * Calculates the diffs between data maps passed in to {@link
org.jboss.cache.CacheListener#nodeModified(org.jboss.cache.Fqn,boolean,boolean,org.jboss.cache.CacheListener.ModificationType,java.util.Map)}
- * before and after modification. This only makes sense if the modification type is
{@link org.jboss.cache.CacheListener.ModificationType#PUT_MAP}.
- * Refer to {@link
org.jboss.cache.CacheListener#nodeModified(org.jboss.cache.Fqn,boolean,boolean,org.jboss.cache.CacheListener.ModificationType,Map)}.
+ * Calculates the diffs between data maps passed in to {@link
org.jboss.cache.notifications.event.NodeModifiedEvent#getData()}
+ * before and after modification. This only makes sense if the modification type is
{@link org.jboss.cache.notifications.event.NodeModifiedEvent.ModificationType#PUT_MAP}.
+ * Refer to {@link org.jboss.cache.notifications.event.NodeModifiedEvent} and {@link
org.jboss.cache.notifications.annotation.NodeModified}.
*
* @param pre map of data before the node was modified
* @param post Map of data after the node was modified
Added:
core/trunk/src/test/java/org/jboss/cache/invalidation/VersionInconsistencyTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/invalidation/VersionInconsistencyTest.java
(rev 0)
+++
core/trunk/src/test/java/org/jboss/cache/invalidation/VersionInconsistencyTest.java 2007-09-18
16:43:17 UTC (rev 4484)
@@ -0,0 +1,97 @@
+package org.jboss.cache.invalidation;
+
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.AfterTest;
+import org.jboss.cache.Cache;
+import org.jboss.cache.DefaultCacheFactory;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.Node;
+import org.jboss.cache.NodeSPI;
+import org.jboss.cache.optimistic.DefaultDataVersion;
+import org.jboss.cache.misc.TestingUtil;
+import org.jboss.cache.transaction.DummyTransactionManagerLookup;
+import org.jboss.cache.config.Configuration;
+
+import javax.transaction.TransactionManager;
+import javax.transaction.Transaction;
+
+/**
+ * This test simulates the problem described in JBCACHE-1155
+ *
+ * @author <a href="mailto:manik@jboss.org">Manik Surtani</a>
+ * @since 2.1.0
+ */
+@Test(groups = {"functional"})
+public class VersionInconsistencyTest
+{
+ private Cache cache1, cache2;
+ private TransactionManager tm1, tm2;
+ private Fqn node = Fqn.fromString("/a");
+
+ @BeforeTest
+ public void setUp()
+ {
+ cache1 = DefaultCacheFactory.getInstance().createCache(false);
+ cache2 = DefaultCacheFactory.getInstance().createCache(false);
+
+ cache1.getConfiguration().setCacheMode(Configuration.CacheMode.INVALIDATION_SYNC);
+ cache2.getConfiguration().setCacheMode(Configuration.CacheMode.INVALIDATION_SYNC);
+
+
cache1.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC);
+
cache2.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC);
+
+
cache1.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName());
+
cache2.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName());
+
+ cache1.start();
+ cache2.start();
+
+ tm1 = cache1.getConfiguration().getRuntimeConfig().getTransactionManager();
+ tm2 = cache2.getConfiguration().getRuntimeConfig().getTransactionManager();
+
+ TestingUtil.blockUntilViewsReceived(1000, cache1, cache2);
+ }
+
+ @AfterTest
+ public void tearDown()
+ {
+ cache1.stop();
+ cache2.stop();
+ }
+
+ public void dataInconsistency() throws Exception
+ {
+ tm1.begin();
+ cache1.put(node, "k", "v-older");
+ Transaction t1 = tm1.suspend();
+
+ tm2.begin();
+ cache2.put(node, "k", "v-newer");
+ tm2.commit();
+
+ tm1.resume(t1);
+ try
+ {
+ tm1.commit();
+ assert false : "Should not be allowed to commit with older data!!";
+ }
+ catch (Exception good)
+ {
+ }
+
+ // the NEWER version of the data should be available, not the OLDER one.
+
+ Object val = cache1.get(node, "k");
+ System.out.println("val = " + val);
+ assert val == null : "Older data should not have committed";
+
+ val = cache2.get(node, "k");
+ System.out.println("val = " + val);
+ assert val.equals("v-newer");
+
+ // test node versions
+ NodeSPI n = (NodeSPI) cache1.getRoot().getChild(node);
+ assert ((DefaultDataVersion) n.getVersion()).getRawVersion() == 1 : "Version
should be 1";
+ }
+}