You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by mr...@apache.org on 2015/06/10 11:24:36 UTC
svn commit: r1684612 - in /jackrabbit/oak/branches/1.2: ./
oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/
oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/
Author: mreutegg
Date: Wed Jun 10 09:24:36 2015
New Revision: 1684612
URL: http://svn.apache.org/r1684612
Log:
OAK-2620: Release merge lock before branch is reset
Merged revision 1684601 from trunk
Modified:
jackrabbit/oak/branches/1.2/ (props changed)
jackrabbit/oak/branches/1.2/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBranch.java
jackrabbit/oak/branches/1.2/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentRootBuilder.java
jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java
Propchange: jackrabbit/oak/branches/1.2/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Wed Jun 10 09:24:36 2015
@@ -1,3 +1,3 @@
/jackrabbit/oak/branches/1.0:1665962
-/jackrabbit/oak/trunk:1672350,1672468,1672537,1672603,1672642,1672644,1672834-1672835,1673351,1673410,1673414-1673415,1673436,1673644,1673662-1673664,1673669,1673695,1673738,1673787,1673791,1674046,1674065,1674075,1674107,1674228,1674780,1674880,1675054-1675055,1675319,1675332,1675354,1675357,1675382,1675555,1675566,1675593,1676198,1676237,1676407,1676458,1676539,1676670,1676693,1676703,1676725,1677579,1677581,1677609,1677611,1677774,1677788,1677797,1677804,1677806,1677939,1677991,1678173,1678323,1678758,1678938,1678954,1679144,1679165,1679191,1679235,1679958,1680182,1680222,1680232,1680236,1680461,1680633,1680643,1680805-1680806,1680903,1681282,1681767,1681918,1682218,1682235,1682437,1682494,1682555,1682855,1682904,1683089,1683213,1683249,1683278,1683323,1683687,1684174-1684175,1684186,1684376,1684442,1684561,1684570
+/jackrabbit/oak/trunk:1672350,1672468,1672537,1672603,1672642,1672644,1672834-1672835,1673351,1673410,1673414-1673415,1673436,1673644,1673662-1673664,1673669,1673695,1673738,1673787,1673791,1674046,1674065,1674075,1674107,1674228,1674780,1674880,1675054-1675055,1675319,1675332,1675354,1675357,1675382,1675555,1675566,1675593,1676198,1676237,1676407,1676458,1676539,1676670,1676693,1676703,1676725,1677579,1677581,1677609,1677611,1677774,1677788,1677797,1677804,1677806,1677939,1677991,1678173,1678323,1678758,1678938,1678954,1679144,1679165,1679191,1679235,1679958,1680182,1680222,1680232,1680236,1680461,1680633,1680643,1680805-1680806,1680903,1681282,1681767,1681918,1682218,1682235,1682437,1682494,1682555,1682855,1682904,1683089,1683213,1683249,1683278,1683323,1683687,1684174-1684175,1684186,1684376,1684442,1684561,1684570,1684601
/jackrabbit/trunk:1345480
Modified: jackrabbit/oak/branches/1.2/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBranch.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBranch.java?rev=1684612&r1=1684611&r2=1684612&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.2/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBranch.java (original)
+++ jackrabbit/oak/branches/1.2/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBranch.java Wed Jun 10 09:24:36 2015
@@ -191,6 +191,14 @@ class DocumentNodeStoreBranch implements
//------------------------------< internal >--------------------------------
+ /**
+ * For test purposes only!
+ */
+ @Nonnull
+ ReadWriteLock getMergeLock() {
+ return mergeLock;
+ }
+
@Nonnull
private NodeState merge0(@Nonnull CommitHook hook,
@Nonnull CommitInfo info,
@@ -212,28 +220,17 @@ class DocumentNodeStoreBranch implements
}
}
try {
- final long start = perfLogger.start();
- Lock lock = acquireMergeLock(exclusive);
- try {
- perfLogger.end(start, 1, "Merge - Acquired lock");
- return branchState.merge(checkNotNull(hook), checkNotNull(info));
- } catch (CommitFailedException e) {
- LOG.trace("Merge Error", e);
- ex = e;
- // only retry on merge failures. these may be caused by
- // changes introduce by a commit hook and may be resolved
- // by a rebase and running the hook again
- if (!e.isOfType(MERGE)) {
- throw e;
- }
- } finally {
- if (lock != null) {
- lock.unlock();
- }
+ return branchState.merge(checkNotNull(hook),
+ checkNotNull(info), exclusive);
+ } catch (CommitFailedException e) {
+ LOG.trace("Merge Error", e);
+ ex = e;
+ // only retry on merge failures. these may be caused by
+ // changes introduce by a commit hook and may be resolved
+ // by a rebase and running the hook again
+ if (!e.isOfType(MERGE)) {
+ throw e;
}
- } catch (InterruptedException e) {
- throw new CommitFailedException(OAK, 1,
- "Unable to acquire merge lock", e);
}
}
// if we get here retrying failed
@@ -249,21 +246,30 @@ class DocumentNodeStoreBranch implements
* @param exclusive whether to acquire the merge lock exclusive.
* @return the acquired merge lock or {@code null} if the operation timed
* out.
- * @throws InterruptedException if the current thread is interrupted while
- * acquiring the lock
+ * @throws CommitFailedException if the current thread is interrupted while
+ * acquiring the lock
*/
@CheckForNull
private Lock acquireMergeLock(boolean exclusive)
- throws InterruptedException {
+ throws CommitFailedException {
+ final long start = perfLogger.start();
Lock lock;
if (exclusive) {
lock = mergeLock.writeLock();
} else {
lock = mergeLock.readLock();
}
- boolean acquired = lock.tryLock(maxLockTryTimeMS, MILLISECONDS);
- if (!acquired) {
- String mode = exclusive ? "exclusive" : "shared";
+ boolean acquired;
+ try {
+ acquired = lock.tryLock(maxLockTryTimeMS, MILLISECONDS);
+ } catch (InterruptedException e) {
+ throw new CommitFailedException(OAK, 1,
+ "Unable to acquire merge lock", e);
+ }
+ String mode = exclusive ? "exclusive" : "shared";
+ if (acquired) {
+ perfLogger.end(start, 1, "Merge - Acquired lock ({})", mode);
+ } else {
LOG.info("Time out while acquiring merge lock ({})", mode);
lock = null;
}
@@ -409,6 +415,8 @@ class DocumentNodeStoreBranch implements
*
* @param hook the commit hook to run.
* @param info the associated commit info.
+ * @param exclusive whether the merge lock must be acquired exclusively
+ * or shared while performing the merge.
* @return the result of the merge.
* @throws CommitFailedException if a commit hook rejected the changes
* or the actual merge operation failed. An implementation must
@@ -416,8 +424,9 @@ class DocumentNodeStoreBranch implements
* indicate the cause of the exception.
*/
@Nonnull
- abstract NodeState merge(
- @Nonnull CommitHook hook, @Nonnull CommitInfo info)
+ abstract NodeState merge(@Nonnull CommitHook hook,
+ @Nonnull CommitInfo info,
+ boolean exclusive)
throws CommitFailedException;
}
@@ -428,7 +437,7 @@ class DocumentNodeStoreBranch implements
* <ul>
* <li>{@link InMemory} on {@link #setRoot(NodeState)} if the new root differs
* from the current base</li>.
- * <li>{@link Merged} on {@link #merge(CommitHook, CommitInfo)}</li>
+ * <li>{@link Merged} on {@link BranchState#merge(CommitHook, CommitInfo, boolean)}</li>
* </ul>
*/
private class Unmodified extends BranchState {
@@ -461,7 +470,9 @@ class DocumentNodeStoreBranch implements
@Override
@Nonnull
- NodeState merge(@Nonnull CommitHook hook, @Nonnull CommitInfo info) {
+ NodeState merge(@Nonnull CommitHook hook,
+ @Nonnull CommitInfo info,
+ boolean exclusive) {
branchState = new Merged(base);
return base;
}
@@ -476,7 +487,7 @@ class DocumentNodeStoreBranch implements
* <li>{@link Unmodified} on {@link #setRoot(NodeState)} if the new root is the same
* as the base of this branch or
* <li>{@link Persisted} otherwise.
- * <li>{@link Merged} on {@link #merge(CommitHook, CommitInfo)}</li>
+ * <li>{@link Merged} on {@link BranchState#merge(CommitHook, CommitInfo, boolean)}</li>
* </ul>
*/
private class InMemory extends BranchState {
@@ -520,20 +531,31 @@ class DocumentNodeStoreBranch implements
@Override
@Nonnull
- NodeState merge(@Nonnull CommitHook hook, @Nonnull CommitInfo info)
+ NodeState merge(@Nonnull CommitHook hook,
+ @Nonnull CommitInfo info,
+ boolean exclusive)
throws CommitFailedException {
checkNotNull(hook);
checkNotNull(info);
- rebase();
- NodeState toCommit = hook.processCommit(base, head, info);
+ Lock lock = acquireMergeLock(exclusive);
try {
- NodeState newHead = DocumentNodeStoreBranch.this.persist(toCommit, base, info);
- branchState = new Merged(base);
- return newHead;
- } catch(DocumentStoreException e) {
- throw new CommitFailedException(MERGE, 1, "Failed to merge changes to the underlying store", e);
- } catch (Exception e) {
- throw new CommitFailedException(OAK, 1, "Failed to merge changes to the underlying store", e);
+ rebase();
+ NodeState toCommit = hook.processCommit(base, head, info);
+ try {
+ NodeState newHead = DocumentNodeStoreBranch.this.persist(toCommit, base, info);
+ branchState = new Merged(base);
+ return newHead;
+ } catch(DocumentStoreException e) {
+ throw new CommitFailedException(MERGE, 1,
+ "Failed to merge changes to the underlying store", e);
+ } catch (Exception e) {
+ throw new CommitFailedException(OAK, 1,
+ "Failed to merge changes to the underlying store", e);
+ }
+ } finally {
+ if (lock != null) {
+ lock.unlock();
+ }
}
}
}
@@ -544,8 +566,8 @@ class DocumentNodeStoreBranch implements
* <p>
* Transitions to:
* <ul>
- * <li>{@link ResetFailed} on failed reset in {@link #merge(CommitHook, CommitInfo)}</li>
- * <li>{@link Merged} on successful {@link #merge(CommitHook, CommitInfo)}</li>
+ * <li>{@link ResetFailed} on failed reset in {@link BranchState#merge(CommitHook, CommitInfo, boolean)}</li>
+ * <li>{@link Merged} on successful {@link BranchState#merge(CommitHook, CommitInfo, boolean)}</li>
* </ul>
*/
private class Persisted extends BranchState {
@@ -618,10 +640,12 @@ class DocumentNodeStoreBranch implements
@Override
@Nonnull
NodeState merge(@Nonnull final CommitHook hook,
- @Nonnull final CommitInfo info)
+ @Nonnull final CommitInfo info,
+ boolean exclusive)
throws CommitFailedException {
boolean success = false;
DocumentNodeState previousHead = head;
+ Lock lock = acquireMergeLock(exclusive);
try {
rebase();
previousHead = head;
@@ -642,6 +666,9 @@ class DocumentNodeStoreBranch implements
throw new CommitFailedException(MERGE, 1,
"Failed to merge changes to the underlying store", e);
} finally {
+ if (lock != null) {
+ lock.unlock();
+ }
if (!success) {
resetBranch(head, previousHead);
}
@@ -700,7 +727,9 @@ class DocumentNodeStoreBranch implements
@Override
@Nonnull
- NodeState merge(@Nonnull CommitHook hook, @Nonnull CommitInfo info) {
+ NodeState merge(@Nonnull CommitHook hook,
+ @Nonnull CommitInfo info,
+ boolean exclusive) {
throw new IllegalStateException("Branch has already been merged");
}
}
@@ -747,7 +776,9 @@ class DocumentNodeStoreBranch implements
*/
@Nonnull
@Override
- NodeState merge(@Nonnull CommitHook hook, @Nonnull CommitInfo info)
+ NodeState merge(@Nonnull CommitHook hook,
+ @Nonnull CommitInfo info,
+ boolean exclusive)
throws CommitFailedException {
throw ex;
}
Modified: jackrabbit/oak/branches/1.2/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentRootBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentRootBuilder.java?rev=1684612&r1=1684611&r2=1684612&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.2/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentRootBuilder.java (original)
+++ jackrabbit/oak/branches/1.2/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentRootBuilder.java Wed Jun 10 09:24:36 2015
@@ -95,7 +95,7 @@ class DocumentRootBuilder extends Abstra
@Override
protected void updated() {
- if (updates++ > UPDATE_LIMIT) {
+ if (++updates > UPDATE_LIMIT) {
purge();
}
}
Modified: jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java?rev=1684612&r1=1684611&r2=1684612&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java (original)
+++ jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java Wed Jun 10 09:24:36 2015
@@ -50,6 +50,8 @@ import java.util.concurrent.CountDownLat
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
@@ -64,6 +66,8 @@ import org.apache.jackrabbit.oak.api.Typ
import org.apache.jackrabbit.oak.plugins.commit.AnnotatingConflictHandler;
import org.apache.jackrabbit.oak.plugins.commit.ConflictHook;
import org.apache.jackrabbit.oak.plugins.commit.ConflictValidatorProvider;
+import org.apache.jackrabbit.oak.plugins.document.UpdateOp.Key;
+import org.apache.jackrabbit.oak.plugins.document.UpdateOp.Operation;
import org.apache.jackrabbit.oak.plugins.document.cache.CacheInvalidationStats;
import org.apache.jackrabbit.oak.plugins.document.memory.MemoryDocumentStore;
import org.apache.jackrabbit.oak.plugins.document.util.TimingDocumentStoreWrapper;
@@ -1815,6 +1819,75 @@ public class DocumentNodeStoreTest {
ns.dispose();
}
+ // OAK-2620
+ @Test
+ public void nonBlockingReset() throws Exception {
+ final List<String> failure = Lists.newArrayList();
+ final AtomicReference<ReentrantReadWriteLock> mergeLock
+ = new AtomicReference<ReentrantReadWriteLock>();
+ MemoryDocumentStore store = new MemoryDocumentStore() {
+ @Override
+ public <T extends Document> T findAndUpdate(Collection<T> collection,
+ UpdateOp update) {
+ for (Map.Entry<Key, Operation> entry : update.getChanges().entrySet()) {
+ if (entry.getKey().getName().equals(NodeDocument.COLLISIONS)) {
+ ReentrantReadWriteLock rwLock = mergeLock.get();
+ if (rwLock.getReadHoldCount() > 0
+ || rwLock.getWriteHoldCount() > 0) {
+ failure.add("Branch reset still holds merge lock");
+ break;
+ }
+ }
+ }
+ return super.findAndUpdate(collection, update);
+ }
+ };
+ DocumentNodeStore ds = new DocumentMK.Builder()
+ .setDocumentStore(store)
+ .setAsyncDelay(0).getNodeStore();
+ ds.setMaxBackOffMillis(0); // do not retry merges
+
+ DocumentNodeState root = ds.getRoot();
+ final DocumentNodeStoreBranch b = ds.createBranch(root);
+ // branch state is now Unmodified
+
+ assertTrue(b.getMergeLock() instanceof ReentrantReadWriteLock);
+ mergeLock.set((ReentrantReadWriteLock) b.getMergeLock());
+
+ NodeBuilder builder = root.builder();
+ builder.child("foo");
+ b.setRoot(builder.getNodeState());
+ // branch state is now InMemory
+ builder.child("bar");
+ b.setRoot(builder.getNodeState());
+ // branch state is now Persisted
+
+ try {
+ b.merge(new CommitHook() {
+ @Nonnull
+ @Override
+ public NodeState processCommit(NodeState before,
+ NodeState after,
+ CommitInfo info)
+ throws CommitFailedException {
+ NodeBuilder foo = after.builder().child("foo");
+ for (int i = 0; i <= DocumentRootBuilder.UPDATE_LIMIT; i++) {
+ foo.setProperty("prop", i);
+ }
+ throw new CommitFailedException("Fail", 0, "");
+ }
+ }, CommitInfo.EMPTY);
+ } catch (CommitFailedException e) {
+ // expected
+ }
+
+ ds.dispose();
+
+ for (String s : failure) {
+ fail(s);
+ }
+ }
+
private static DocumentNodeState asDocumentNodeState(NodeState state) {
if (!(state instanceof DocumentNodeState)) {
throw new IllegalArgumentException("Not a DocumentNodeState");