Author: mreutegg
Date: Mon Nov 25 09:43:55 2013
New Revision: 1545190

URL: http://svn.apache.org/r1545190
Log:
OAK-1202: Retry Persisted merge
- Introduced a MERGE type in CommitFailedException to distinguish it from other 
commit failures
- Make sure KernelNodeStoreBranch throws appropriate exceptions on failures
- Leverage MicroKernel.reset() when Persisted.merge() fails
- Introduced a new branch state ResetFailed when the branch cannot be reset
- Moved the retry logic to AbstractNodeStoreBranch
- Removed the now duplicate retry logic in MongoNodeStoreBranch

Modified:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/CommitFailedException.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoMK.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeStore.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeStoreBranch.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/NodeDocument.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeStoreBranch.java
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/MergeRetryTest.java

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/CommitFailedException.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/CommitFailedException.java?rev=1545190&r1=1545189&r2=1545190&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/CommitFailedException.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/CommitFailedException.java
 Mon Nov 25 09:43:55 2013
@@ -99,6 +99,11 @@ public class CommitFailedException exten
     public static final String LABEL_EXISTS = "LabelExists";
 
     /**
+     * Type name for merge errors.
+     */
+    public static final String MERGE = "Merge";
+
+    /**
      * Unsupported operation or feature
      */
     public static final String UNSUPPORTED = "Unsupported";

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java?rev=1545190&r1=1545189&r2=1545190&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java
 Mon Nov 25 09:43:55 2013
@@ -264,4 +264,9 @@ public class KernelNodeStore implements 
     KernelNodeState merge(KernelNodeState branchHead) {
         return getRootState(kernel.merge(branchHead.getRevision(), null));
     }
+
+    KernelNodeState reset(KernelNodeState branchHead, KernelNodeState 
ancestor) {
+        return getRootState(kernel.reset(
+                branchHead.getRevision(), ancestor.getRevision()));
+    }
 }

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java?rev=1545190&r1=1545189&r2=1545190&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java
 Mon Nov 25 09:43:55 2013
@@ -33,6 +33,7 @@ import org.apache.jackrabbit.oak.spi.sta
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.jackrabbit.oak.api.CommitFailedException.MERGE;
 
 /**
  * {@code NodeStoreBranch} based on {@link MicroKernel} branching and merging.
@@ -90,8 +91,21 @@ public class KernelNodeStoreBranch exten
 
     @Override
     protected KernelNodeState merge(KernelNodeState branchHead,
-                                    CommitInfo info) {
-        return store.merge(branchHead);
+                                    CommitInfo info)
+            throws CommitFailedException {
+        try {
+            return store.merge(branchHead);
+        } catch (MicroKernelException e) {
+            throw new CommitFailedException(MERGE, 1,
+                    "Failed to merge changes to the underlying MicroKernel", 
e);
+        }
+    }
+
+    @Nonnull
+    @Override
+    protected KernelNodeState reset(@Nonnull KernelNodeState branchHead,
+                                    @Nonnull KernelNodeState ancestor) {
+        return store.reset(branchHead, ancestor);
     }
 
     @Override
@@ -127,8 +141,7 @@ public class KernelNodeStoreBranch exten
         try {
             return super.merge(hook, info);
         } catch (MicroKernelException e) {
-            throw new CommitFailedException(
-                    "Kernel", 1,
+            throw new CommitFailedException(MERGE, 1,
                     "Failed to merge changes to the underlying MicroKernel", 
e);
         } finally {
             mergeLock.unlock();

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoMK.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoMK.java?rev=1545190&r1=1545189&r2=1545190&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoMK.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoMK.java
 Mon Nov 25 09:43:55 2013
@@ -37,6 +37,7 @@ import org.apache.jackrabbit.mk.json.Jso
 import org.apache.jackrabbit.mk.json.JsopStream;
 import org.apache.jackrabbit.mk.json.JsopTokenizer;
 import org.apache.jackrabbit.mk.json.JsopWriter;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.cache.CacheLIRS;
 import org.apache.jackrabbit.oak.cache.CacheStats;
 import org.apache.jackrabbit.oak.cache.CacheValue;
@@ -434,7 +435,11 @@ public class MongoMK implements MicroKer
         if (!revision.isBranch()) {
             throw new MicroKernelException("Not a branch: " + 
branchRevisionId);
         }
-        return nodeStore.merge(revision, null).toString();
+        try {
+            return nodeStore.merge(revision, null).toString();
+        } catch (CommitFailedException e) {
+            throw new MicroKernelException(e.getMessage(), e);
+        }
     }
 
     @Override

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeStore.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeStore.java?rev=1545190&r1=1545189&r2=1545190&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeStore.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeStore.java
 Mon Nov 25 09:43:55 2013
@@ -19,6 +19,7 @@ package org.apache.jackrabbit.oak.plugin
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
+import static org.apache.jackrabbit.oak.api.CommitFailedException.MERGE;
 
 import java.io.Closeable;
 import java.io.IOException;
@@ -799,11 +800,11 @@ public final class MongoNodeStore
             UpdateOp rootOp = commit.getUpdateOperationForNode("/");
             // clear collisions
             Iterator<Revision> it = 
b.getCommits().tailSet(ancestor).iterator();
-            // first revision is the ancestor
+            // first revision is the ancestor (tailSet is inclusive)
             // do not clear collision for this revision
             it.next();
             while (it.hasNext()) {
-                NodeDocument.unsetCollision(rootOp, it.next());
+                NodeDocument.removeCollision(rootOp, it.next());
             }
             rev = apply(commit);
             success = true;
@@ -818,7 +819,8 @@ public final class MongoNodeStore
     }
 
     @Nonnull
-    Revision merge(@Nonnull Revision branchHead, @Nullable CommitInfo info) {
+    Revision merge(@Nonnull Revision branchHead, @Nullable CommitInfo info)
+            throws CommitFailedException {
         Branch b = getBranches().getBranch(branchHead);
         Revision base = branchHead;
         if (b != null) {
@@ -842,8 +844,8 @@ public final class MongoNodeStore
                     b.applyTo(getPendingModifications(), commit.getRevision());
                     getBranches().remove(b);
                 } else {
-                    // TODO: use non-MK exception type
-                    throw new MicroKernelException("Conflicting concurrent 
change. Update operation failed: " + op);
+                    throw new CommitFailedException(MERGE, 2,
+                            "Conflicting concurrent change. Update operation 
failed: " + op);
                 }
             } else {
                 // no commits in this branch -> do nothing

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeStoreBranch.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeStoreBranch.java?rev=1545190&r1=1545189&r2=1545190&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeStoreBranch.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeStoreBranch.java
 Mon Nov 25 09:43:55 2013
@@ -17,12 +17,9 @@
 package org.apache.jackrabbit.oak.plugins.mongomk;
 
 import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 
-import org.apache.jackrabbit.mk.api.MicroKernelException;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.spi.commit.ChangeDispatcher;
-import org.apache.jackrabbit.oak.spi.commit.CommitHook;
 import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
 import org.apache.jackrabbit.oak.spi.state.AbstractNodeStoreBranch;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -33,12 +30,6 @@ import org.apache.jackrabbit.oak.spi.sta
 public class MongoNodeStoreBranch
         extends AbstractNodeStoreBranch<MongoNodeStore, MongoNodeState> {
 
-    /**
-     * TODO: what is a reasonable value?
-     * TODO: fall back to pessimistic approach? how does this work in a 
cluster?
-     */
-    private static final int MERGE_RETRIES = 10;
-
     public MongoNodeStoreBranch(MongoNodeStore store,
                                 MongoNodeState base) {
         super(store, new ChangeDispatcher(store.getRoot()), base);
@@ -61,10 +52,19 @@ public class MongoNodeStoreBranch
     }
 
     @Override
-    protected MongoNodeState merge(MongoNodeState branchHead, CommitInfo info) 
{
+    protected MongoNodeState merge(MongoNodeState branchHead, CommitInfo info)
+            throws CommitFailedException {
         return store.getRoot(store.merge(branchHead.getRevision(), info));
     }
 
+    @Nonnull
+    @Override
+    protected MongoNodeState reset(@Nonnull MongoNodeState branchHead,
+                                   @Nonnull MongoNodeState ancestor) {
+        return store.getRoot(store.reset(branchHead.getRevision(),
+                ancestor.getRevision())).setBranch();
+    }
+
     @Override
     protected MongoNodeState persist(final NodeState toPersist,
                                      final MongoNodeState base,
@@ -106,25 +106,6 @@ public class MongoNodeStoreBranch
         }, base, null);
     }
 
-    //--------------------< AbstractNodeStoreBranch 
>---------------------------
-
-    @Nonnull
-    @Override
-    public NodeState merge(@Nonnull CommitHook hook, @Nullable CommitInfo info)
-            throws CommitFailedException {
-        MicroKernelException ex = null;
-        for (int i = 0; i < MERGE_RETRIES; i++) {
-            try {
-                return super.merge(hook, info);
-            } catch (MicroKernelException e) {
-                ex = e;
-            }
-        }
-        throw new CommitFailedException(
-                "Kernel", 1,
-                "Failed to merge changes to the underlying store", ex);
-    }
-
     //------------------------------< internal 
>--------------------------------
 
     /**

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/NodeDocument.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/NodeDocument.java?rev=1545190&r1=1545189&r2=1545190&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/NodeDocument.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/NodeDocument.java
 Mon Nov 25 09:43:55 2013
@@ -843,9 +843,9 @@ public class NodeDocument extends Docume
         checkNotNull(op).unsetMapEntry(REVISIONS, checkNotNull(revision));
     }
 
-    public static void unsetCollision(@Nonnull UpdateOp op,
-                                      @Nonnull Revision revision) {
-        checkNotNull(op).unsetMapEntry(COLLISIONS, checkNotNull(revision));
+    public static void removeCollision(@Nonnull UpdateOp op,
+                                       @Nonnull Revision revision) {
+        checkNotNull(op).removeMapEntry(COLLISIONS, checkNotNull(revision));
     }
 
     public static void setLastRev(@Nonnull UpdateOp op,

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeStoreBranch.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeStoreBranch.java?rev=1545190&r1=1545189&r2=1545190&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeStoreBranch.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeStoreBranch.java
 Mon Nov 25 09:43:55 2013
@@ -21,6 +21,8 @@ import java.util.Random;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.apache.jackrabbit.oak.api.CommitFailedException.MERGE;
+import static org.apache.jackrabbit.oak.api.CommitFailedException.OAK;
 import static org.apache.jackrabbit.oak.commons.PathUtils.elements;
 import static org.apache.jackrabbit.oak.commons.PathUtils.getName;
 import static org.apache.jackrabbit.oak.commons.PathUtils.getParentPath;
@@ -105,8 +107,22 @@ public abstract class AbstractNodeStoreB
      * @param branchHead the head of the branch to merge.
      * @param info the commit info or <code>null</code> if none available.
      * @return the result state of the merge.
+     * @throws CommitFailedException if the merge fails. The type of the
+     *                    exception will be {@code 
CommitFailedException.MERGE}.
      */
-    protected abstract N merge(N branchHead, CommitInfo info);
+    protected abstract N merge(N branchHead, CommitInfo info)
+            throws CommitFailedException;
+
+    /**
+     * Resets the branch head to the given ancestor on the same branch.
+     *
+     * @param branchHead the head of the branch to reset.
+     * @param ancestor the state of the branch to reset to.
+     * @return the state of the reset branch. This is not necessarily the same
+     *         instance as {@code ancestor} but is guaranteed to be equal to 
it.
+     */
+    @Nonnull
+    protected abstract N reset(@Nonnull N branchHead, @Nonnull N ancestor);
 
     /**
      * Persists the changes between <code>toPersist</code> and 
<code>base</code>
@@ -231,7 +247,30 @@ public abstract class AbstractNodeStoreB
     @Override
     public NodeState merge(@Nonnull CommitHook hook, @Nullable CommitInfo info)
             throws CommitFailedException {
-        return branchState.merge(checkNotNull(hook), info);
+        CommitFailedException ex = null;
+        for (long backoff = 100; backoff < maximumBackoff; backoff *= 2) {
+            if (ex != null) {
+                try {
+                    Thread.sleep(backoff, RANDOM.nextInt(1000000));
+                } catch (InterruptedException ie) {
+                    // ignore
+                    Thread.interrupted();
+                }
+            }
+            try {
+                return branchState.merge(checkNotNull(hook), info);
+            } catch (CommitFailedException 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;
+                }
+            }
+        }
+        // if we get here retrying failed
+        throw ex;
     }
 
     @Override
@@ -294,7 +333,10 @@ public abstract class AbstractNodeStoreB
          * @param hook the commit hook to run.
          * @param info the associated commit info.
          * @return the result of the merge.
-         * @throws CommitFailedException if a commit hook rejected the changes.
+         * @throws CommitFailedException if a commit hook rejected the changes
+         *          or the actual merge operation failed. An implementation 
must
+         *          use the appropriate type in {@code CommitFailedException} 
to
+         *          indicate the cause of the exception.
          */
         @Nonnull
         abstract NodeState merge(@Nonnull CommitHook hook, @Nullable 
CommitInfo info)
@@ -403,31 +445,18 @@ public abstract class AbstractNodeStoreB
         NodeState merge(@Nonnull CommitHook hook, CommitInfo info)
                 throws CommitFailedException {
             try {
-                CommitFailedException ex = null;
-                for (long backoff = 100; backoff < maximumBackoff; backoff *= 
2) {
-                    if (ex != null) {
-                        try {
-                            Thread.sleep(backoff, RANDOM.nextInt(1000000));
-                        } catch (InterruptedException ie) {
-                            // ignore
-                            Thread.interrupted();
-                        }
-                    }
-                    rebase();
-                    dispatcher.contentChanged(base, null);
-                    NodeState toCommit = 
checkNotNull(hook).processCommit(base, head);
-                    try {
-                        NodeState newHead = 
AbstractNodeStoreBranch.this.persist(toCommit, base, info);
-                        dispatcher.contentChanged(newHead, info);
-                        branchState = new Merged(base);
-                        return newHead;
-                    } catch (Exception e) {
-                        ex = new CommitFailedException(
-                                "Kernel", 1,
-                                "Failed to merge changes to the underlying 
store", e);
-                    }
+                rebase();
+                dispatcher.contentChanged(base, null);
+                NodeState toCommit = checkNotNull(hook).processCommit(base, 
head);
+                try {
+                    NodeState newHead = 
AbstractNodeStoreBranch.this.persist(toCommit, base, info);
+                    dispatcher.contentChanged(newHead, info);
+                    branchState = new Merged(base);
+                    return newHead;
+                } catch (Exception e) {
+                    throw new CommitFailedException(MERGE, 1,
+                            "Failed to merge changes to the underlying store", 
e);
                 }
-                throw ex;
             } finally {
                 dispatcher.contentChanged(getRoot(), null);
             }
@@ -442,7 +471,8 @@ public abstract class AbstractNodeStoreB
      * <ul>
      *     <li>{@link Unmodified} on {@link #setRoot(NodeState)} if the new 
root is the same
      *         as the base of this branch.
-     *     <li>{@link Merged} on {@link #merge(CommitHook, CommitInfo)}</li>
+     *     <li>{@link ResetFailed} on failed reset in {@link 
#merge(CommitHook, CommitInfo)}</li>
+     *     <li>{@link Merged} on successful {@link #merge(CommitHook, 
CommitInfo)}</li>
      * </ul>
      */
     private class Persisted extends BranchState {
@@ -499,21 +529,31 @@ public abstract class AbstractNodeStoreB
 
         @Override
         @Nonnull
-        NodeState merge(@Nonnull CommitHook hook, CommitInfo info) throws 
CommitFailedException {
+        NodeState merge(@Nonnull CommitHook hook, CommitInfo info)
+                throws CommitFailedException {
             try {
                 rebase();
                 dispatcher.contentChanged(base, null);
                 NodeState toCommit = checkNotNull(hook).processCommit(base, 
head);
-                if (toCommit.equals(base)) {
-                    branchState = new Merged(base);
-                    return base;
-                } else {
-                    head = AbstractNodeStoreBranch.this.persist(toCommit, 
head, info);
-                    NodeState newRoot = 
AbstractNodeStoreBranch.this.merge(head, info);
-                    dispatcher.contentChanged(newRoot, info);
-                    branchState = new Merged(base);
-                    return newRoot;
+                N newRoot = AbstractNodeStoreBranch.this.persist(toCommit, 
head, info);
+                boolean success = false;
+                try {
+                    newRoot = AbstractNodeStoreBranch.this.merge(newRoot, 
info);
+                    success = true;
+                } finally {
+                    if (!success) {
+                        try {
+                            AbstractNodeStoreBranch.this.reset(newRoot, head);
+                        } catch (Exception e) {
+                            CommitFailedException ex = new 
CommitFailedException(
+                                            OAK, 100, "Branch reset failed", 
e);
+                            branchState = new ResetFailed(base, ex);
+                        }
+                    }
                 }
+                branchState = new Merged(base);
+                dispatcher.contentChanged(newRoot, info);
+                return newRoot;
             } finally {
                 dispatcher.contentChanged(getRoot(), null);
             }
@@ -563,4 +603,52 @@ public abstract class AbstractNodeStoreB
         }
     }
 
+    /**
+     * Instances of this class represent a branch with persisted changes and
+     * a failed attempt to reset changes.
+     * <p>
+     * Transitions to: none.
+     */
+    private class ResetFailed extends BranchState {
+
+        /**
+         * The exception of the failed reset.
+         */
+        private final CommitFailedException ex;
+
+        protected ResetFailed(N base, CommitFailedException e) {
+            super(base);
+            this.ex = e;
+        }
+
+        @Nonnull
+        @Override
+        NodeState getHead() {
+            throw new IllegalStateException("Branch with failed reset");
+        }
+
+        @Override
+        void setRoot(NodeState root) {
+            throw new IllegalStateException("Branch with failed reset");
+        }
+
+        @Override
+        void rebase() {
+            throw new IllegalStateException("Branch with failed reset");
+        }
+
+        /**
+         * Always throws the {@code CommitFailedException} passed to the
+         * constructor of this branch state.
+         *
+         * @throws CommitFailedException the exception of the failed reset.
+         */
+        @Nonnull
+        @Override
+        NodeState merge(@Nonnull CommitHook hook, @Nullable CommitInfo info)
+                throws CommitFailedException {
+            throw ex;
+        }
+    }
+
 }

Modified: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/MergeRetryTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/MergeRetryTest.java?rev=1545190&r1=1545189&r2=1545190&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/MergeRetryTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/MergeRetryTest.java
 Mon Nov 25 09:43:55 2013
@@ -91,7 +91,6 @@ public class MergeRetryTest {
     /**
      * Test for OAK-1202
      */
-    @Ignore
     @Test
     public void retryPersisted() throws Exception {
         MemoryDocumentStore ds = new MemoryDocumentStore();


Reply via email to