[jbosscache-dev] Problems with node locking semantics in JBC1.4.1.GA
Brian Stansberry
brian.stansberry at jboss.com
Sun Jan 28 16:11:01 EST 2007
I played around with one way to deal with this.
Speaking in db isolation terms, let's say a non-repeatable-read means a
tx reads a node's *data map* twice and gets different results. A phantom
read means a tx reads a node's *child map* twice and gets different
results. Conceptually, a JBC node represents both a "row" (data map) and
a query result set (children map -- set of rows that share a common
characteristic that the parent represents). (I know this analogy is
inexact, but, ...).
In 1.4.0.SP1, REPEATABLE_READ meant you wouldn't get
non-repeatable-reads, but could get phantom reads. With 1.4.1, you also
don't get phantom reads (at least with respect to a node's immediate
children). But, in many cases this behavior is unneeded and undesirable,
and JBC no longer offers the old behavior.
I attempted to remedy this with a new IsolationLevel
REPEATABLE_DATA_READ, which basically provides the 1.4.0 semantics.
This was actually very easy to do.
1) LockStrategyFactory creates the same LockStrategy for
REPEATABLE_DATA_READ as for REPEATABLE_READ -- hence lock behavior at
the node level is unaffected.
2) PessimisticLockInterceptor uses the new level when deciding whether
to WL a parent before adding/removing a node. Manik nicely encapsulated
that logic, so it was simple to change. Here's a diff to the head of
Branch_JBossCache_1_4_0:
### Eclipse Workspace Patch 1.0
#P JBossCache
Index: src/org/jboss/cache/interceptors/PessimisticLockInterceptor.java
===================================================================
RCS file:
/cvsroot/jboss/JBossCache/src/org/jboss/cache/interceptors/PessimisticLo
ckInterceptor.java,v
retrieving revision 1.20.2.8
diff -u -r1.20.2.8 PessimisticLockInterceptor.java
--- src/org/jboss/cache/interceptors/PessimisticLockInterceptor.java
14 Dec 2006 12:51:48 -0000 1.20.2.8
+++ src/org/jboss/cache/interceptors/PessimisticLockInterceptor.java
28 Jan 2007 21:08:55 -0000
@@ -41,6 +41,8 @@
public class PessimisticLockInterceptor extends Interceptor
{
TransactionTable tx_table = null;
+
+ boolean writeLockOnChildInsertRemove = true;
/**
* Map<Object, java.util.List>. Keys = threads, values = lists of
locks held by that thread
@@ -55,6 +57,7 @@
tx_table = cache.getTransactionTable();
lock_table = cache.getLockTable();
lock_acquisition_timeout = cache.getLockAcquisitionTimeout();
+ writeLockOnChildInsertRemove = (cache.getIsolationLevelClass() !=
IsolationLevel.REPEATABLE_DATA_READ);
}
@@ -323,12 +326,14 @@
private boolean writeLockNeeded(int lock_type, int currentNodeIndex,
int treeNodeSize, boolean isRemoveOperation, boolean createIfNotExists,
Fqn targetFqn, Fqn currentFqn)
{
- if (isRemoveOperation && currentNodeIndex == treeNodeSize - 2)
- return true; // we're doing a remove and we've reached the
PARENT node of the target to be removed.
-
- if (!isTargetNode(currentNodeIndex, treeNodeSize) &&
!cache.exists(new Fqn(currentFqn, targetFqn.get(currentNodeIndex + 1))))
- return createIfNotExists; // we're at a node in the tree, not
yet at the target node, and we need to create the next node. So we need
a WL here.
-
+ if (writeLockOnChildInsertRemove)
+ {
+ if (isRemoveOperation && currentNodeIndex == treeNodeSize - 2)
+ return true; // we're doing a remove and we've reached the
PARENT node of the target to be removed.
+
+ if (!isTargetNode(currentNodeIndex, treeNodeSize) &&
!cache.exists(new Fqn(currentFqn, targetFqn.get(currentNodeIndex + 1))))
+ return createIfNotExists; // we're at a node in the tree,
not yet at the target node, and we need to create the next node. So we
need a WL here.
+ }
return lock_type == DataNode.LOCK_TYPE_WRITE &&
isTargetNode(currentNodeIndex, treeNodeSize) && (createIfNotExists ||
isRemoveOperation); //normal operation, write lock explicitly requested
and this is the target to be written to.
}
jbosscache-dev-bounces at lists.jboss.org wrote:
> In 1.4.1.GA, we introduced node locking semantics that
> weren't there in the previous few releases (and maybe never
> were there). This is causing significant integration issues.
>
> Specifically, before inserting or removing a child node, we
> acquire a WL on the parent. Previously, only a RL was acquired.
>
> I'm concerned about anomalies this might cause in apps that
> intentionally or unintentionally depends on the old behavior.
> For example, the AS http session repl code assumes it has
> freedom to add/remove cache nodes that represent a session
> underneath the parent that represents the webapp. Now these
> activities will block until any activity on any other session
> is complete. With FIELD granularity, that's a big issue, as
> locks are held throughout the web request. It will likely
> cause issues with other granularities as well. (We already
> saw a minor issue in a support case when a customer tried to upgrade
> 4.0.5 to 1.4.1).
>
> This definitely breaks the Hibernate (and thus EJB3 entity bean
> clustering) integration with 1.4.1.GA. Basically, assume
> there is an entity Person, with a child collection
> "children". Hibernate caches the entity, and later caches
> the child collection in a child node.
>
> 1) Transaction tx = tm.begin();
> 2) cache.get("/Person/Person#1/children", ITEM); // returns
> null -- not cached yet
>
> 3) read db to get the ids of the collection elements
>
> 4) tx.suspend();
> 5) cache.putFailFast("/Person/Person#1/children", ITEM, data); 6)
> tx.resume();
>
> This fails in step 5, because with the tx suspended, the
> putFailFast() call cannot acquire the WL on /Person/Person#1
> that it needs to insert /Person/Person#1/children.
>
> Hmm, an EJB3 unit test shows that problem occuring, but I
> assume even simple entity caching will break:
>
> 1) Transaction tx = tm.begin();
> 2) cache.get("/Person/Person#2", ITEM); // returns null --
> not cached yet
>
> 3) read db to get the Person
>
> 4) tx.suspend();
> 5) cache.putFailFast("/Person/Person#1", ITEM, data); 6) tx.resume();
>
> We need a quick workaround for this in the 1.4 branch, as
> it's a blocker for EJB3 RC10 and AS 4.2.
>
> Brian Stansberry
> Lead, AS Clustering
> JBoss, a division of Red Hat
> Ph: 510-396-3864
> skype: bstansberry
>
> _______________________________________________
> jbosscache-dev mailing list
> jbosscache-dev at lists.jboss.org
> https://lists.jboss.org/mailman/listinfo/jbosscache-dev
More information about the jbosscache-dev
mailing list