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 2016/01/07 13:46:36 UTC

svn commit: r1723532 [4/5] - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/ oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/d...

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/RevisionsKey.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/RevisionsKey.java?rev=1723532&r1=1723531&r2=1723532&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/RevisionsKey.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/RevisionsKey.java Thu Jan  7 12:46:35 2016
@@ -20,7 +20,7 @@ import javax.annotation.Nonnull;
 
 import org.apache.jackrabbit.oak.cache.CacheValue;
 import org.apache.jackrabbit.oak.plugins.document.Revision;
-import org.apache.jackrabbit.oak.plugins.document.StableRevisionComparator;
+import org.apache.jackrabbit.oak.plugins.document.RevisionVector;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
@@ -29,16 +29,16 @@ import static com.google.common.base.Pre
  */
 public final class RevisionsKey implements CacheValue, Comparable<RevisionsKey> {
 
-    private final Revision r1, r2;
+    private final RevisionVector r1, r2;
 
-    public RevisionsKey(Revision r1, Revision r2) {
+    public RevisionsKey(RevisionVector r1, RevisionVector r2) {
         this.r1 = checkNotNull(r1);
         this.r2 = checkNotNull(r2);
     }
 
     @Override
     public int getMemory() {
-        return 88;
+        return 32 + r1.getMemory() + r2.getMemory();
     }
 
     @Override
@@ -65,11 +65,11 @@ public final class RevisionsKey implemen
     }
 
     public int compareTo(@Nonnull RevisionsKey k) {
-        int c = StableRevisionComparator.INSTANCE.compare(r1, k.r1);
+        int c = r1.compareTo(k.r1);
         if (c != 0) {
             return c;
         }
-        return StableRevisionComparator.INSTANCE.compare(r2, k.r2);
+        return r2.compareTo(k.r2);
     }
 
     public static RevisionsKey fromString(String s) {
@@ -78,7 +78,7 @@ public final class RevisionsKey implemen
             throw new IllegalArgumentException(s);
         }
         return new RevisionsKey(
-                Revision.fromString(s.substring(0, idx)),
-                Revision.fromString(s.substring(idx + 1)));
+                RevisionVector.fromString(s.substring(0, idx)),
+                RevisionVector.fromString(s.substring(idx + 1)));
     }
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/Utils.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/Utils.java?rev=1723532&r1=1723531&r2=1723532&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/Utils.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/Utils.java Thu Jan  7 12:46:35 2016
@@ -46,7 +46,7 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.plugins.document.DocumentStore;
 import org.apache.jackrabbit.oak.plugins.document.NodeDocument;
 import org.apache.jackrabbit.oak.plugins.document.Revision;
-import org.apache.jackrabbit.oak.plugins.document.RevisionContext;
+import org.apache.jackrabbit.oak.plugins.document.RevisionVector;
 import org.apache.jackrabbit.oak.plugins.document.StableRevisionComparator;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -467,19 +467,6 @@ public class Utils {
     }
 
     /**
-     * Checks that revision x is newer than another revision.
-     *
-     * @param x the revision to check
-     * @param previous the presumed earlier revision
-     * @return true if x is newer
-     */
-    public static boolean isRevisionNewer(@Nonnull RevisionContext context,
-                                          @Nonnull Revision x,
-                                          @Nonnull Revision previous) {
-        return context.getRevisionComparator().compare(x, previous) > 0;
-    }
-
-    /**
      * Returns the revision with the newer timestamp or {@code null} if both
      * revisions are {@code null}. The implementation will return the first
      * revision if both have the same timestamp.
@@ -517,6 +504,43 @@ public class Utils {
     }
 
     /**
+     * Returns the revision with the older timestamp or {@code null} if both
+     * revisions are {@code null}. The implementation will return the first
+     * revision if both have the same timestamp.
+     *
+     * @param a the first revision (or {@code null}).
+     * @param b the second revision (or {@code null}).
+     * @return the revision with the older timestamp.
+     */
+    @CheckForNull
+    public static Revision min(@Nullable Revision a, @Nullable Revision b) {
+        return min(a, b, StableRevisionComparator.INSTANCE);
+    }
+
+    /**
+     * Returns the revision which is considered older or {@code null} if
+     * both revisions are {@code null}. The implementation will return the first
+     * revision if both are considered equal. The comparison is done using the
+     * provided comparator.
+     *
+     * @param a the first revision (or {@code null}).
+     * @param b the second revision (or {@code null}).
+     * @param c the comparator.
+     * @return the revision considered more recent.
+     */
+    @CheckForNull
+    public static Revision min(@Nullable Revision a,
+                               @Nullable Revision b,
+                               @Nonnull Comparator<Revision> c) {
+        if (a == null) {
+            return b;
+        } else if (b == null) {
+            return a;
+        }
+        return c.compare(a, b) <= 0 ? a : b;
+    }
+
+    /**
      * Returns an {@link Iterable} over all {@link NodeDocument}s in the given
      * store. The returned {@linkplain Iterable} does not guarantee a consistent
      * view on the store. it may return documents that have been added to the
@@ -686,4 +710,34 @@ public class Utils {
             return n.longValue();
         }
     }
+
+    /**
+     * Returns the minimum timestamp to use for a query for child documents that
+     * have been modified between {@code fromRev} and {@code toRev}.
+     *
+     * @param fromRev the from revision.
+     * @param toRev the to revision.
+     * @param minRevisions the minimum revisions of foreign cluster nodes. These
+     *                     are derived from the startTime of a cluster node.
+     * @return the minimum timestamp.
+     */
+    public static long getMinTimestampForDiff(@Nonnull RevisionVector fromRev,
+                                              @Nonnull RevisionVector toRev,
+                                              @Nonnull RevisionVector minRevisions) {
+        // make sure we have minimum revisions for all known cluster nodes
+        fromRev = fromRev.pmax(minRevisions);
+        toRev = toRev.pmax(minRevisions);
+        // keep only revision entries that changed
+        RevisionVector from = fromRev.difference(toRev);
+        RevisionVector to = toRev.difference(fromRev);
+        // now calculate minimum timestamp
+        long min = Long.MAX_VALUE;
+        for (Revision r : from) {
+            min = Math.min(r.getTimestamp(), min);
+        }
+        for (Revision r : to) {
+            min = Math.min(r.getTimestamp(), min);
+        }
+        return min;
+    }
 }

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/AmnesiaDiffCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/AmnesiaDiffCache.java?rev=1723532&r1=1723531&r2=1723532&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/AmnesiaDiffCache.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/AmnesiaDiffCache.java Thu Jan  7 12:46:35 2016
@@ -35,8 +35,8 @@ class AmnesiaDiffCache extends DiffCache
     }
 
     @Override
-    public String getChanges(@Nonnull Revision from,
-                             @Nonnull Revision to,
+    public String getChanges(@Nonnull RevisionVector from,
+                             @Nonnull RevisionVector to,
                              @Nonnull String path,
                              @Nullable Loader loader) {
         if (loader != null) {
@@ -47,7 +47,7 @@ class AmnesiaDiffCache extends DiffCache
 
     @Nonnull
     @Override
-    public Entry newEntry(@Nonnull Revision from, @Nonnull Revision to, boolean local) {
+    public Entry newEntry(@Nonnull RevisionVector from, @Nonnull RevisionVector to, boolean local) {
         return new Entry() {
             @Override
             public void append(@Nonnull String path, @Nonnull String changes) {

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/BranchTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/BranchTest.java?rev=1723532&r1=1723531&r2=1723532&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/BranchTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/BranchTest.java Thu Jan  7 12:46:35 2016
@@ -28,9 +28,9 @@ public class BranchTest {
 
     @Test
     public void getModifiedPathsUntil() {
-        UnmergedBranches branches = new UnmergedBranches(StableRevisionComparator.INSTANCE);
+        UnmergedBranches branches = new UnmergedBranches();
 
-        Revision base = Revision.newRevision(1);
+        RevisionVector base = new RevisionVector(Revision.newRevision(1));
         Revision c1 = Revision.newRevision(1).asBranchRevision();
         Branch b = branches.create(base, c1, null);
 
@@ -43,7 +43,7 @@ public class BranchTest {
         bc2.track("/bar");
 
         Revision c3 = Revision.newRevision(1).asBranchRevision();
-        b.rebase(c3, Revision.newRevision(1));
+        b.rebase(c3, new RevisionVector(Revision.newRevision(1)));
 
         Revision c4 = Revision.newRevision(1).asBranchRevision();
         b.addCommit(c4);

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CheckpointsTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CheckpointsTest.java?rev=1723532&r1=1723531&r2=1723532&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CheckpointsTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CheckpointsTest.java Thu Jan  7 12:46:35 2016
@@ -22,9 +22,11 @@ import java.util.Collections;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.jackrabbit.oak.plugins.document.memory.MemoryDocumentStore;
 import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
 import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.stats.Clock;
 import org.junit.Before;
 import org.junit.Rule;
@@ -33,6 +35,7 @@ import org.junit.Test;
 import com.google.common.collect.ImmutableMap;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertNull;
@@ -70,7 +73,7 @@ public class CheckpointsTest {
         Revision r1 = null;
         for(int i = 0; i < Checkpoints.CLEANUP_INTERVAL; i++){
             r1 = Revision.fromString(store.checkpoint(expiryTime));
-            store.setHeadRevision(Revision.newRevision(0));
+            store.setRoot(new RevisionVector(Revision.newRevision(store.getClusterId())));
         }
         assertEquals(r1, store.getCheckpoints().getOldestRevisionToKeep());
         assertEquals(Checkpoints.CLEANUP_INTERVAL, store.getCheckpoints().size());
@@ -204,4 +207,148 @@ public class CheckpointsTest {
         assertNotNull(info);
         assertEquals(props, info);
     }
+
+    @Test
+    public void parseInfo() {
+        long expires = System.currentTimeMillis();
+        // initial 1.0 format: only expiry time
+        Checkpoints.Info info = Checkpoints.Info.fromString(String.valueOf(expires));
+        assertEquals(expires, info.getExpiryTime());
+        // 1.2 format: json with expiry and info map
+        String infoString = "{\"expires\":\"" + expires + "\",\"foo\":\"bar\"}";
+        info = Checkpoints.Info.fromString(infoString);
+        assertEquals(expires, info.getExpiryTime());
+        assertEquals(Collections.singleton("foo"), info.get().keySet());
+        assertEquals("bar", info.get().get("foo"));
+        // 1.4 format: json with expiry, revision vector and info map
+        Revision r1 = new Revision(1, 0, 1);
+        Revision r2 = new Revision(1, 0, 2);
+        RevisionVector rv = new RevisionVector(r1, r2);
+        infoString = "{\"expires\":\"" + expires +
+                "\",\"rv\":\"" + rv.toString() +
+                "\",\"foo\":\"bar\"}";
+        info = Checkpoints.Info.fromString(infoString);
+        assertEquals(expires, info.getExpiryTime());
+        assertEquals(Collections.singleton("foo"), info.get().keySet());
+        assertEquals("bar", info.get().get("foo"));
+        assertEquals(rv, info.getCheckpoint());
+        assertEquals(infoString, info.toString());
+    }
+
+    @Test
+    public void crossClusterNodeCheckpoint() throws Exception {
+        // use an async delay to ensure DocumentNodeStore.suspendUntil() works
+        // but set it to a high value and control background ops manually in
+        // this test
+        final int asyncDelay = (int) TimeUnit.MINUTES.toMillis(1);
+        DocumentStore store = new MemoryDocumentStore();
+        final DocumentNodeStore ns1 = builderProvider.newBuilder()
+                .setDocumentStore(store).setAsyncDelay(asyncDelay).getNodeStore();
+        final DocumentNodeStore ns2 = builderProvider.newBuilder()
+                .setDocumentStore(store).setAsyncDelay(asyncDelay).getNodeStore();
+
+        // create node on ns1
+        NodeBuilder builder = ns1.getRoot().builder();
+        builder.child("foo");
+        ns1.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        // make visible on ns2
+        ns1.runBackgroundOperations();
+        ns2.runBackgroundOperations();
+        // create checkpoint on ns1
+        String cp1 = ns1.checkpoint(Long.MAX_VALUE);
+        // retrieve checkpoint on ns2
+        NodeState root = ns2.retrieve(cp1);
+        assertNotNull(root);
+        assertTrue(root.hasChildNode("foo"));
+        ns2.release(cp1);
+
+        // create node on ns1
+        builder = ns1.getRoot().builder();
+        builder.child("bar");
+        ns1.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        // create checkpoint when 'bar' is not yet visible to ns2
+        final String cp2 = ns1.checkpoint(Long.MAX_VALUE);
+        // retrieve checkpoint on ns2
+        final NodeState state[] = new NodeState[1];
+        Thread t = new Thread(new Runnable() {
+            @Override
+            public void run() {
+                state[0] = ns2.retrieve(cp2);
+            }
+        });
+        t.start();
+        ns1.runBackgroundOperations();
+        ns2.runBackgroundOperations();
+        t.join();
+        assertNotNull(state[0]);
+        assertTrue(state[0].hasChildNode("bar"));
+    }
+
+    @Test
+    public void crossClusterCheckpointNewClusterNode() throws Exception {
+        DocumentStore store = new MemoryDocumentStore();
+        DocumentNodeStore ns1 = builderProvider.newBuilder()
+                .setDocumentStore(store).setAsyncDelay(0).getNodeStore();
+
+        // create 'foo' on ns1
+        NodeBuilder b1 = ns1.getRoot().builder();
+        b1.child("foo");
+        ns1.merge(b1, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        // checkpoint sees 'foo' but not 'bar'
+        String checkpoint = ns1.checkpoint(Long.MAX_VALUE);
+
+        // create 'bar' on ns1
+        b1 = ns1.getRoot().builder();
+        b1.child("bar");
+        ns1.merge(b1, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        // make visible
+        ns1.runBackgroundOperations();
+
+        // now start second node store
+        DocumentNodeStore ns2 = builderProvider.newBuilder()
+                .setDocumentStore(store).setAsyncDelay(0).getNodeStore();
+        NodeBuilder b2 = ns2.getRoot().builder();
+        b2.child("baz");
+        ns2.merge(b2, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        NodeState root = ns2.retrieve(checkpoint);
+        assertNotNull(root);
+        assertTrue(root.hasChildNode("foo"));
+        assertFalse(root.hasChildNode("bar"));
+        assertFalse(root.hasChildNode("baz"));
+    }
+
+    @Test
+    public void crossClusterReadOldCheckpoint() throws Exception {
+        DocumentStore store = new MemoryDocumentStore();
+        DocumentNodeStore ns1 = builderProvider.newBuilder()
+                .setDocumentStore(store).setAsyncDelay(0).getNodeStore();
+
+        NodeBuilder b1 = ns1.getRoot().builder();
+        b1.child("foo");
+        ns1.merge(b1, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        ns1.runBackgroundOperations();
+
+        // manually create a check point in 1.2 format
+        Revision headRev = Revision.fromString(ns1.getHeadRevision().toString());
+        long expires = Long.MAX_VALUE;
+        String data = "{\"expires\":\"" + expires + "\"}";
+        UpdateOp update = new UpdateOp("checkpoint", false);
+        update.setMapEntry("data", headRev, data);
+        store.createOrUpdate(Collection.SETTINGS, update);
+
+        // now start second node store
+        DocumentNodeStore ns2 = builderProvider.newBuilder()
+                .setDocumentStore(store).setAsyncDelay(0).getNodeStore();
+        NodeBuilder b2 = ns2.getRoot().builder();
+        b2.child("baz");
+        ns2.merge(b2, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        NodeState root = ns2.retrieve(headRev.toString());
+        assertNotNull(root);
+        assertTrue(root.hasChildNode("foo"));
+        assertFalse(root.hasChildNode("baz"));
+    }
 }

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ClusterRevisionComparisonTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ClusterRevisionComparisonTest.java?rev=1723532&r1=1723531&r2=1723532&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ClusterRevisionComparisonTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ClusterRevisionComparisonTest.java Thu Jan  7 12:46:35 2016
@@ -92,8 +92,6 @@ public class ClusterRevisionComparisonTe
         c1.invalidateNodeCache("/a/c2" , ((DocumentNodeState)c1ns1.getChildNode("a")).getLastRevision());
         c1.invalidateNodeCache("/a/c3" , ((DocumentNodeState)c1ns1.getChildNode("a")).getLastRevision());
 
-        //Revision comparator purge by moving in future
-        clock.waitUntil(clock.getTime() + DocumentNodeStore.REMEMBER_REVISION_ORDER_MILLIS * 2);
         runBgOps(c1);
 
         NodeState a = c1ns1.getChildNode("a");
@@ -139,8 +137,6 @@ public class ClusterRevisionComparisonTe
         c1.invalidateNodeCache("/a/c1" , ((DocumentNodeState)a).getLastRevision());
         c1.invalidateNodeCache("/a/c2" , ((DocumentNodeState)a).getLastRevision());
 
-        //Revision comparator purge by moving in future
-        clock.waitUntil(clock.getTime() + DocumentNodeStore.REMEMBER_REVISION_ORDER_MILLIS * 2);
         runBgOps(c1);
 
         assertTrue("/a/c1 disappeared", a.hasChildNode("c1"));

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ClusterTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ClusterTest.java?rev=1723532&r1=1723531&r2=1723532&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ClusterTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ClusterTest.java Thu Jan  7 12:46:35 2016
@@ -216,9 +216,9 @@ public class ClusterTest {
 
         mk3.runBackgroundOperations(); // pick up changes from mk2
 
-        DocumentNodeState base = ns3.getNode("/", Revision.fromString(base3));
+        DocumentNodeState base = ns3.getNode("/", RevisionVector.fromString(base3));
         assertNotNull(base);
-        NodeState branchHead = ns3.getNode("/", Revision.fromString(b3));
+        NodeState branchHead = ns3.getNode("/", RevisionVector.fromString(b3));
         assertNotNull(branchHead);
         TrackingDiff diff = new TrackingDiff();
         branchHead.compareAgainstBaseState(base, diff);

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitQueueTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitQueueTest.java?rev=1723532&r1=1723531&r2=1723532&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitQueueTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitQueueTest.java Thu Jan  7 12:46:35 2016
@@ -68,14 +68,15 @@ public class CommitQueueTest {
         AtomicBoolean running = new AtomicBoolean(true);
 
         Closeable observer = store.addObserver(new Observer() {
-            private Revision before = new Revision(0, 0, store.getClusterId());
+            private RevisionVector before = new RevisionVector(
+                    new Revision(0, 0, store.getClusterId()));
 
             @Override
             public void contentChanged(@Nonnull NodeState root, @Nullable CommitInfo info) {
                 DocumentNodeState after = (DocumentNodeState) root;
-                Revision r = after.getRevision();
+                RevisionVector r = after.getRevision();
                 LOG.debug("seen: {}", r);
-                if (r.compareRevisionTime(before) < 0) {
+                if (r.compareTo(before) < 0) {
                     exceptions.add(new Exception(
                             "Inconsistent revision sequence. Before: " +
                                     before + ", after: " + r));
@@ -187,7 +188,7 @@ public class CommitQueueTest {
         final DocumentNodeStore ds = builderProvider.newBuilder().getNodeStore();
 
         // simulate start of a branch commit
-        Commit c = ds.newCommit(ds.getHeadRevision().asBranchRevision(), null);
+        Commit c = ds.newCommit(ds.getHeadRevision().asBranchRevision(ds.getClusterId()), null);
 
         Thread t = new Thread(new Runnable() {
             @Override
@@ -212,16 +213,15 @@ public class CommitQueueTest {
 
     @Test
     public void suspendUntil() throws Exception {
-        final AtomicReference<Revision> headRevision = new AtomicReference<Revision>();
+        final AtomicReference<RevisionVector> headRevision = new AtomicReference<RevisionVector>();
         RevisionContext context = new DummyRevisionContext() {
             @Nonnull
             @Override
-            public Revision getHeadRevision() {
+            public RevisionVector getHeadRevision() {
                 return headRevision.get();
             }
         };
-        headRevision.set(context.newRevision());
-
+        headRevision.set(new RevisionVector(context.newRevision()));
         final CommitQueue queue = new CommitQueue(context);
 
         final Revision newHeadRev = context.newRevision();
@@ -247,7 +247,7 @@ public class CommitQueueTest {
         // must still be suspended
         assertEquals(1, queue.numSuspendedThreads());
 
-        headRevision.set(newHeadRev);
+        headRevision.set(new RevisionVector(newHeadRev));
         queue.headRevisionChanged();
         // must still be suspended
         assertEquals(1, queue.numSuspendedThreads());
@@ -261,15 +261,15 @@ public class CommitQueueTest {
 
     @Test
     public void suspendUntilTimeout() throws Exception {
-        final AtomicReference<Revision> headRevision = new AtomicReference<Revision>();
+        final AtomicReference<RevisionVector> headRevision = new AtomicReference<RevisionVector>();
         RevisionContext context = new DummyRevisionContext() {
             @Nonnull
             @Override
-            public Revision getHeadRevision() {
+            public RevisionVector getHeadRevision() {
                 return headRevision.get();
             }
         };
-        headRevision.set(context.newRevision());
+        headRevision.set(new RevisionVector(context.newRevision()));
         final CommitQueue queue = new CommitQueue(context);
         queue.setSuspendTimeoutMillis(0);
 
@@ -288,15 +288,15 @@ public class CommitQueueTest {
 
     @Test
     public void concurrentSuspendUntil() throws Exception {
-        final AtomicReference<Revision> headRevision = new AtomicReference<Revision>();
+        final AtomicReference<RevisionVector> headRevision = new AtomicReference<RevisionVector>();
         RevisionContext context = new DummyRevisionContext() {
             @Nonnull
             @Override
-            public Revision getHeadRevision() {
+            public RevisionVector getHeadRevision() {
                 return headRevision.get();
             }
         };
-        headRevision.set(context.newRevision());
+        headRevision.set(new RevisionVector(context.newRevision()));
 
         List<Thread> threads = new ArrayList<Thread>();
         List<Revision> allRevisions = new ArrayList<Revision>();

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitTest.java?rev=1723532&r1=1723531&r2=1723532&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitTest.java Thu Jan  7 12:46:35 2016
@@ -57,7 +57,8 @@ public class CommitTest {
         // this commit should fail
         Commit c = ns.newCommit(ns.getHeadRevision(), null);
         try {
-            c.addNode(new DocumentNodeState(ns, "/foo/baz", c.getRevision()));
+            c.addNode(new DocumentNodeState(ns, "/foo/baz",
+                    new RevisionVector(c.getRevision())));
             UpdateOp op = c.getUpdateOperationForNode("/bar");
             op.setMapEntry("p", c.getRevision(), "v");
             try {

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CountingTieredDiffCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CountingTieredDiffCache.java?rev=1723532&r1=1723531&r2=1723532&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CountingTieredDiffCache.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CountingTieredDiffCache.java Thu Jan  7 12:46:35 2016
@@ -56,8 +56,8 @@ public class CountingTieredDiffCache ext
     }
 
     @Override
-    public String getChanges(@Nonnull Revision from,
-                             @Nonnull Revision to,
+    public String getChanges(@Nonnull RevisionVector from,
+                             @Nonnull RevisionVector to,
                              @Nonnull String path,
                              @Nullable Loader loader) {
         return super.getChanges(from, to, path, new CountingLoader(loader));

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStateTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStateTest.java?rev=1723532&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStateTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStateTest.java Thu Jan  7 12:46:35 2016
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.document;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class DocumentNodeStateTest {
+
+    @Rule
+    public DocumentMKBuilderProvider builderProvider = new DocumentMKBuilderProvider();
+
+    @Test
+    public void getMemory() {
+        DocumentNodeStore store = builderProvider.newBuilder().getNodeStore();
+        RevisionVector rv = new RevisionVector(Revision.newRevision(1));
+        DocumentNodeState state = new DocumentNodeState(store, "/foo", rv);
+        assertEquals(232, state.getMemory());
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStateTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java?rev=1723532&r1=1723531&r2=1723532&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java Thu Jan  7 12:46:35 2016
@@ -19,7 +19,6 @@ package org.apache.jackrabbit.oak.plugin
 import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.apache.jackrabbit.oak.api.CommitFailedException.CONSTRAINT;
 import static org.apache.jackrabbit.oak.plugins.document.Collection.NODES;
-import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore.REMEMBER_REVISION_ORDER_MILLIS;
 import static org.apache.jackrabbit.oak.plugins.document.NodeDocument.MODIFIED_IN_SECS;
 import static org.apache.jackrabbit.oak.plugins.document.NodeDocument.MODIFIED_IN_SECS_RESOLUTION;
 import static org.apache.jackrabbit.oak.plugins.document.NodeDocument.NUM_REVS_THRESHOLD;
@@ -99,7 +98,6 @@ import org.apache.jackrabbit.oak.spi.sta
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
 import org.apache.jackrabbit.oak.stats.Clock;
 import org.junit.After;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.slf4j.Logger;
@@ -266,7 +264,7 @@ public class DocumentNodeStoreTest {
         builder.child("deletedNode").remove();
         merge(store, builder);
 
-        final Revision head = store.getHeadRevision();
+        final RevisionVector head = store.getHeadRevision();
 
         Thread writer = new Thread(new Runnable() {
             @Override
@@ -274,8 +272,8 @@ public class DocumentNodeStoreTest {
                 try {
                     Revision r = store.newRevision();
                     Commit c = new Commit(store, r, head, null);
-                    c.addNode(new DocumentNodeState(store, "/newConflictingNode", r));
-                    c.addNode(new DocumentNodeState(store, "/deletedNode", r));
+                    c.addNode(new DocumentNodeState(store, "/newConflictingNode", new RevisionVector(r)));
+                    c.addNode(new DocumentNodeState(store, "/deletedNode", new RevisionVector(r)));
                     c.updateProperty("/updateNode", "foo", "baz");
                     c.apply();
                 } catch (DocumentStoreException e) {
@@ -292,8 +290,8 @@ public class DocumentNodeStoreTest {
         // commit will succeed and add collision marker to writer commit
         Revision r = store.newRevision();
         Commit c = new Commit(store, r, head, null);
-        c.addNode(new DocumentNodeState(store, "/newConflictingNode", r));
-        c.addNode(new DocumentNodeState(store, "/newNonConflictingNode", r));
+        c.addNode(new DocumentNodeState(store, "/newConflictingNode", new RevisionVector(r)));
+        c.addNode(new DocumentNodeState(store, "/newNonConflictingNode", new RevisionVector(r)));
         c.apply();
         // allow writer to continue
         s.release();
@@ -356,7 +354,7 @@ public class DocumentNodeStoreTest {
                 .setDocumentStore(docStore).setAsyncDelay(0)
                 .setClusterId(1).getNodeStore();
         ns1.getRoot();
-        Revision r1 = ns1.getHeadRevision();
+        Revision r1 = ns1.getHeadRevision().getRevision(ns1.getClusterId());
         ns1.runBackgroundOperations();
         DocumentNodeStore ns2 = builderProvider.newBuilder()
                 .setDocumentStore(docStore).setAsyncDelay(0)
@@ -434,48 +432,6 @@ public class DocumentNodeStoreTest {
         }
     }
 
-    // OAK-1814
-    @Test
-    public void visibilityAfterRevisionComparatorPurge() throws Exception {
-        Clock clock = new Clock.Virtual();
-        clock.waitUntil(System.currentTimeMillis());
-        Revision.setClock(clock);
-        MemoryDocumentStore docStore = new MemoryDocumentStore();
-        DocumentNodeStore nodeStore1 = builderProvider.newBuilder()
-                .setDocumentStore(docStore).setClusterId(1)
-                .setAsyncDelay(0).clock(clock).getNodeStore();
-        nodeStore1.runBackgroundOperations();
-        DocumentNodeStore nodeStore2 = builderProvider.newBuilder()
-                .setDocumentStore(docStore).setClusterId(2)
-                .setAsyncDelay(0).clock(clock).getNodeStore();
-        DocumentNodeStore nodeStore3 = builderProvider.newBuilder()
-                .setDocumentStore(docStore).setClusterId(3)
-                .setAsyncDelay(0).clock(clock).getNodeStore();
-
-        NodeDocument doc = docStore.find(NODES, Utils.getIdFromPath("/"));
-        assertNotNull(doc);
-        Revision created = doc.getLocalDeleted().firstKey();
-        assertEquals(1, created.getClusterId());
-
-        clock.waitUntil(System.currentTimeMillis() +
-                REMEMBER_REVISION_ORDER_MILLIS / 2);
-
-        NodeBuilder builder = nodeStore2.getRoot().builder();
-        builder.setProperty("prop", "value");
-        nodeStore2.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
-        nodeStore2.runBackgroundOperations();
-
-        clock.waitUntil(System.currentTimeMillis() +
-                REMEMBER_REVISION_ORDER_MILLIS + 1000);
-        nodeStore3.runBackgroundOperations();
-
-        doc = docStore.find(NODES, Utils.getIdFromPath("/"));
-        assertNotNull(doc);
-        NodeState state = doc.getNodeAtRevision(nodeStore3,
-                nodeStore3.getHeadRevision(), null);
-        assertNotNull(state);
-    }
-
     @Test
     public void modifiedReset() throws Exception {
         Clock clock = new Clock.Virtual();
@@ -580,7 +536,7 @@ public class DocumentNodeStoreTest {
         builder.child("test").setProperty("prop", "value");
         ns.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
 
-        Revision rev = ns.getHeadRevision();
+        RevisionVector rev = ns.getHeadRevision();
         NodeDocument doc = docStore.find(NODES, Utils.getIdFromPath("/test"));
         assertNotNull(doc);
         DocumentNodeState state = doc.getNodeAtRevision(ns, rev, null);
@@ -677,28 +633,30 @@ public class DocumentNodeStoreTest {
         DocumentNodeStore ns1 = builderProvider.newBuilder().setAsyncDelay(0)
                 .setClusterId(1).setDocumentStore(docStore)
                 .getNodeStore();
+        int cId1 = ns1.getClusterId();
         DocumentNodeStore ns2 = builderProvider.newBuilder().setAsyncDelay(0)
                 .setClusterId(2).setDocumentStore(docStore)
                 .getNodeStore();
+        int cId2 = ns2.getClusterId();
 
         ns1.updateClusterState();
         ns2.updateClusterState();
 
-        assertEquals(0, ns1.getInactiveClusterNodes().size());
-        assertEquals(0, ns2.getInactiveClusterNodes().size());
-        assertEquals(2, ns1.getActiveClusterNodes().size());
-        assertEquals(2, ns2.getActiveClusterNodes().size());
+        assertEquals(0, ns1.getMBean().getInactiveClusterNodes().length);
+        assertEquals(0, ns2.getMBean().getInactiveClusterNodes().length);
+        assertEquals(2, ns1.getMBean().getActiveClusterNodes().length);
+        assertEquals(2, ns2.getMBean().getActiveClusterNodes().length);
 
         ns1.dispose();
 
         ns2.updateClusterState();
 
-        Map<Integer, Long> inactive = ns2.getInactiveClusterNodes();
-        Map<Integer, Long> active = ns2.getActiveClusterNodes();
-        assertEquals(1, inactive.size());
-        assertEquals(1, (int) inactive.keySet().iterator().next());
-        assertEquals(1, active.size());
-        assertEquals(2, (int) active.keySet().iterator().next());
+        String[] inactive = ns2.getMBean().getInactiveClusterNodes();
+        String[] active = ns2.getMBean().getActiveClusterNodes();
+        assertEquals(1, inactive.length);
+        assertTrue(inactive[0].startsWith(cId1 + "="));
+        assertEquals(1, active.length);
+        assertTrue(active[0].startsWith(cId2 + "="));
     }
 
     // OAK-2288
@@ -723,7 +681,7 @@ public class DocumentNodeStoreTest {
 
         NodeDocument doc = docStore.find(NODES, id);
         assertNotNull(doc);
-        Revision rev = doc.getLocalDeleted().firstKey();
+        RevisionVector rev = new RevisionVector(doc.getLocalDeleted().firstKey());
 
         merge(store, builder1);
 
@@ -829,41 +787,6 @@ public class DocumentNodeStoreTest {
         }
     }
 
-    // OAK-2345
-    @Test
-    public void inactiveClusterId() throws Exception {
-        Clock clock = new Clock.Virtual();
-        clock.waitUntil(System.currentTimeMillis());
-        Revision.setClock(clock);
-        MemoryDocumentStore docStore = new MemoryDocumentStore();
-        DocumentNodeStore ns1 = builderProvider.newBuilder()
-                .setDocumentStore(docStore).setClusterId(1)
-                .setAsyncDelay(0).clock(clock).getNodeStore();
-        NodeBuilder builder = ns1.getRoot().builder();
-        builder.child("test");
-        merge(ns1, builder);
-        Revision r = ns1.getHeadRevision();
-        ns1.dispose();
-
-        // start other cluster node
-        DocumentNodeStore ns2 = builderProvider.newBuilder()
-                .setDocumentStore(docStore).setClusterId(2)
-                .setAsyncDelay(0).clock(clock).getNodeStore();
-        assertNotNull(ns2.getRevisionComparator().getRevisionSeen(r));
-        ns2.dispose();
-
-        // wait until revision is old
-        clock.waitUntil(System.currentTimeMillis()
-                + REMEMBER_REVISION_ORDER_MILLIS + 1000);
-
-        // start cluster 2 again
-        ns2 = builderProvider.newBuilder()
-                .setDocumentStore(docStore).setClusterId(2)
-                .setAsyncDelay(0).clock(clock).getNodeStore();
-        // now r is considered old and revisionSeen is null
-        assertNull(ns2.getRevisionComparator().getRevisionSeen(r));
-    }
-
     // OAK-1782
     @Test
     public void diffOnce() throws Exception {
@@ -892,9 +815,11 @@ public class DocumentNodeStoreTest {
         }
         merge(ns, builder);
 
-        final Revision head = ns.getHeadRevision();
-        final Revision to = new Revision(
-                head.getTimestamp() + 1000, 0, head.getClusterId());
+        final RevisionVector head = ns.getHeadRevision();
+        Revision localHead = head.getRevision(ns.getClusterId());
+        assertNotNull(localHead);
+        final RevisionVector to = new RevisionVector(new Revision(
+                localHead.getTimestamp() + 1000, 0, localHead.getClusterId()));
         int numReaders = 10;
         final CountDownLatch ready = new CountDownLatch(numReaders);
         final CountDownLatch go = new CountDownLatch(1);
@@ -954,7 +879,7 @@ public class DocumentNodeStoreTest {
         builder.child("test").remove();
         merge(store, builder);
 
-        Revision removedAt = store.getHeadRevision();
+        RevisionVector removedAt = store.getHeadRevision();
 
         String id = Utils.getIdFromPath("/test");
         int count = 0;
@@ -973,7 +898,7 @@ public class DocumentNodeStoreTest {
         assertNoPreviousDocs(reads);
 
         reads.clear();
-        doc.getValueMap("foo").get(removedAt);
+        doc.getValueMap("foo").get(removedAt.getRevision(store.getClusterId()));
         assertNoPreviousDocs(reads);
     }
 
@@ -1244,7 +1169,7 @@ public class DocumentNodeStoreTest {
         String parentPath = "/:hidden/parent";
         NodeDocument parentDoc = docStore.find(Collection.NODES, Utils.getIdFromPath(parentPath));
         assertFalse("parent node of unseen children must not get deleted",
-                isDocDeleted(parentDoc, store1.getRevisionComparator()));
+                isDocDeleted(parentDoc));
 
         //Test 2 - parent shouldn't be removable if order of operation is:
         //# N1 and N2 know about /:hidden
@@ -1262,7 +1187,7 @@ public class DocumentNodeStoreTest {
 
         parentDoc = docStore.find(Collection.NODES, Utils.getIdFromPath(parentPath));
         assertFalse("parent node of unseen children must not get deleted",
-                isDocDeleted(parentDoc, store2.getRevisionComparator()));
+                isDocDeleted(parentDoc));
 
         store1.runBackgroundOperations();
         store2.runBackgroundOperations();
@@ -1296,7 +1221,7 @@ public class DocumentNodeStoreTest {
         parentPath = "/:hidden/parent1";
         parentDoc = docStore.find(Collection.NODES, Utils.getIdFromPath(parentPath));
         assertFalse("parent node of unseen children must not get deleted",
-                isDocDeleted(parentDoc, store1.getRevisionComparator()));
+                isDocDeleted(parentDoc));
 
         //Test 4 - parent shouldn't be removable if order of operation is:
         //# N1 and N2 know about /:hidden/parent1
@@ -1313,7 +1238,7 @@ public class DocumentNodeStoreTest {
 
         parentDoc = docStore.find(Collection.NODES, Utils.getIdFromPath(parentPath));
         assertFalse("parent node of unseen children must not get deleted",
-                isDocDeleted(parentDoc, store2.getRevisionComparator()));
+                isDocDeleted(parentDoc));
     }
 
     @Test
@@ -1699,7 +1624,6 @@ public class DocumentNodeStoreTest {
     }
 
     // OAK-3646
-    @Ignore("OAK-3646")
     @Test
     public void concurrentChildOperations() throws Exception {
         Clock clock = new Clock.Virtual();
@@ -1742,7 +1666,7 @@ public class DocumentNodeStoreTest {
         // on cluster node 2, remove of child-0 is not yet visible
         List<ChildNodeEntry> children = Lists.newArrayList(ns2.getRoot().getChildNode("foo").getChildNode("bar").getChildNodeEntries());
         assertEquals(2, Iterables.size(children));
-        Revision invalidate = null;
+        RevisionVector invalidate = null;
         for (ChildNodeEntry entry : children) {
             if (entry.getName().equals("child-0")) {
                 invalidate = asDocumentNodeState(entry.getNodeState()).getRevision();
@@ -1753,16 +1677,12 @@ public class DocumentNodeStoreTest {
         // this will make changes from cluster node 1 visible
         ns2.runBackgroundOperations();
 
-        // wait twice the time we remember revision order
-        clock.waitUntil(clock.getTime() + 2 * REMEMBER_REVISION_ORDER_MILLIS);
-        // collect everything older than one hour (time revision order is remembered)
+        // wait two hours
+        clock.waitUntil(clock.getTime() + TimeUnit.HOURS.toMillis(2));
+        // collect everything older than one hour
         // this will remove child-0 and child-1 doc
-        ns1.getVersionGarbageCollector().gc(REMEMBER_REVISION_ORDER_MILLIS, TimeUnit.MILLISECONDS);
+        ns1.getVersionGarbageCollector().gc(1, TimeUnit.HOURS);
 
-        // trigger purge of revisions older than one hour in RevisionComparator
-        // this is usually done by the background read operation, but we
-        // do it explicitly here to make sure it really happens in this test
-        ns2.getRevisionComparator().purge(clock.getTime() - REMEMBER_REVISION_ORDER_MILLIS);
         // forget cache entry for deleted node
         ns2.invalidateNodeCache("/foo/bar/child-0", invalidate);
 
@@ -1772,7 +1692,6 @@ public class DocumentNodeStoreTest {
 
     // OAK-3646
     // similar to previous test but both cluster nodes add a child node
-    @Ignore("OAK-3646")
     @Test
     public void concurrentChildOperations2() throws Exception {
         Clock clock = new Clock.Virtual();
@@ -1814,14 +1733,6 @@ public class DocumentNodeStoreTest {
         // this will make changes from cluster node 1 visible
         ns2.runBackgroundOperations();
 
-        // wait twice the time we remember revision order
-        clock.waitUntil(clock.getTime() + 2 * REMEMBER_REVISION_ORDER_MILLIS);
-
-        // trigger purge of revisions older than one hour in RevisionComparator
-        // this is usually done by the background read operation, but we
-        // do it explicitly here to make sure it really happens in this test
-        ns2.getRevisionComparator().purge(clock.getTime() - REMEMBER_REVISION_ORDER_MILLIS);
-
         children = Lists.newArrayList(ns2.getRoot().getChildNode("foo").getChildNodeEntries());
         assertEquals(2, Iterables.size(children));
     }
@@ -2156,11 +2067,11 @@ public class DocumentNodeStoreTest {
     public void dispatch() throws Exception {
         DocumentNodeStore ns = builderProvider.newBuilder().getNodeStore();
 
-        Revision from = ns.getHeadRevision();
+        RevisionVector from = ns.getHeadRevision();
         NodeBuilder builder = ns.getRoot().builder();
         builder.child("test");
         merge(ns, builder);
-        Revision to = ns.getHeadRevision();
+        RevisionVector to = ns.getHeadRevision();
 
         DiffCache.Entry entry = ns.getDiffCache().newEntry(from, to, true);
         entry.append("/", "-\"foo\"");
@@ -2188,7 +2099,7 @@ public class DocumentNodeStoreTest {
         builder.child("foo").child("child").child("node");
         merge(ns, builder);
 
-        Revision head = ns.getHeadRevision();
+        RevisionVector head = ns.getHeadRevision();
         NodeState child = ns.getRoot().getChildNode("bar").getChildNode("child");
         assertTrue(child instanceof DocumentNodeState);
         DocumentNodeState state = (DocumentNodeState) child;
@@ -2304,7 +2215,9 @@ public class DocumentNodeStoreTest {
         afterTest.compareAgainstBaseState(beforeTest, new DefaultNodeStateDiff());
 
         assertEquals(1, startValues.size());
-        long beforeModified = getModifiedInSecs(before.getRevision().getTimestamp());
+        Revision localHead = before.getRevision().getRevision(ns.getClusterId());
+        assertNotNull(localHead);
+        long beforeModified = getModifiedInSecs(localHead.getTimestamp());
         // startValue must be based on the revision of the before state
         // and not when '/test' was last modified
         assertEquals(beforeModified, (long) startValues.get(0));
@@ -2444,7 +2357,7 @@ public class DocumentNodeStoreTest {
         final List<Commit> commits = new ArrayList<Commit>();
         for (int i = 0; i < 10; i++) {
             Revision revision = ds.newRevision();
-            Commit commit = ds.newCommit(revision, ds.createBranch(root));
+            Commit commit = ds.newCommit(new RevisionVector(revision), ds.createBranch(root));
             commits.add(commit);
             revisions.add(revision);
         }
@@ -2655,6 +2568,80 @@ public class DocumentNodeStoreTest {
         assertTrue(diff.modified.contains("/parent/node-x/child"));
     }
 
+    @Test
+    public void lastRevWithRevisionVector() throws Exception {
+        MemoryDocumentStore store = new MemoryDocumentStore();
+        DocumentNodeStore ns1 = builderProvider.newBuilder()
+                .setDocumentStore(store).setAsyncDelay(0).getNodeStore();
+        DocumentNodeStore ns2 = builderProvider.newBuilder()
+                .setDocumentStore(store).setAsyncDelay(0).getNodeStore();
+
+        NodeBuilder b1 = ns1.getRoot().builder();
+        b1.child("parent");
+        merge(ns1, b1);
+        b1 = ns1.getRoot().builder();
+        NodeBuilder parent = b1.child("parent");
+        parent.setProperty("p", 1);
+        parent.child("child");
+        merge(ns1, b1);
+        ns1.runBackgroundOperations();
+        ns2.runBackgroundOperations();
+
+        NodeBuilder b2 = ns2.getRoot().builder();
+        b2.child("parent").setProperty("p", 2);
+        merge(ns2, b2);
+        ns2.runBackgroundOperations();
+        ns1.runBackgroundOperations();
+
+        assertTrue(ns1.getRoot().getChildNode("parent").hasChildNode("child"));
+    }
+
+    @Test
+    public void branchBaseBeforeClusterJoin() throws Exception {
+        MemoryDocumentStore store = new MemoryDocumentStore();
+        DocumentNodeStore ns1 = builderProvider.newBuilder()
+                .setDocumentStore(store).setAsyncDelay(0).getNodeStore();
+
+        NodeBuilder b1 = ns1.getRoot().builder();
+        b1.child("parent");
+        merge(ns1, b1);
+        ns1.runBackgroundOperations();
+
+        DocumentNodeStore ns2 = builderProvider.newBuilder()
+                .setDocumentStore(store).setAsyncDelay(0).getNodeStore();
+        NodeBuilder b2 = ns2.getRoot().builder();
+        b2.child("parent").child("baz");
+        merge(ns2, b2);
+        ns2.runBackgroundOperations();
+
+        DocumentNodeState root = ns1.getRoot();
+        DocumentNodeStoreBranch b = ns1.createBranch(root);
+        // branch state is now Unmodified
+        NodeBuilder builder = root.builder();
+        builder.child("parent").child("foo");
+        b.setRoot(builder.getNodeState());
+        // branch state is now InMemory
+        builder.child("parent").child("bar");
+        b.setRoot(builder.getNodeState());
+        // branch state is now Persisted
+
+        b.rebase();
+        NodeState parent = b.getHead().getChildNode("parent");
+        assertTrue(parent.exists());
+        assertTrue(parent.hasChildNode("foo"));
+        assertTrue(parent.hasChildNode("bar"));
+        assertFalse(parent.hasChildNode("baz"));
+
+        ns1.runBackgroundOperations();
+
+        b.merge(EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        parent = ns1.getRoot().getChildNode("parent");
+        assertTrue(parent.exists());
+        assertTrue(parent.hasChildNode("foo"));
+        assertTrue(parent.hasChildNode("bar"));
+        assertTrue(parent.hasChildNode("baz"));
+    }
+
     private static DocumentNodeState asDocumentNodeState(NodeState state) {
         if (!(state instanceof DocumentNodeState)) {
             throw new IllegalArgumentException("Not a DocumentNodeState");
@@ -2768,11 +2755,10 @@ public class DocumentNodeStoreTest {
      * @param doc the document to be tested
      * @return latest committed value of _deleted map
      */
-    private boolean isDocDeleted(NodeDocument doc,
-                                 Comparator<Revision> comparator) {
+    private boolean isDocDeleted(NodeDocument doc) {
         boolean latestDeleted = false;
-        SortedMap<Revision, String> localDeleted = Maps.newTreeMap(
-                Collections.reverseOrder(comparator));
+        SortedMap<Revision, String> localDeleted =
+                Maps.newTreeMap(StableRevisionComparator.REVERSE);
         localDeleted.putAll(doc.getLocalDeleted());
 
         for (Map.Entry<Revision, String> entry : localDeleted.entrySet()) {

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentSplitTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentSplitTest.java?rev=1723532&r1=1723531&r2=1723532&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentSplitTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentSplitTest.java Thu Jan  7 12:46:35 2016
@@ -18,7 +18,6 @@ package org.apache.jackrabbit.oak.plugin
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -95,7 +94,7 @@ public class DocumentSplitTest extends B
             assertTrue(doc.isCommitted(rev));
         }
         // check if document is still there
-        assertNotNull(ns.getNode("/", Revision.fromString(head)));
+        assertNotNull(ns.getNode("/", RevisionVector.fromString(head)));
 
         NodeDocument prevDoc = Iterators.getOnlyElement(doc.getAllPreviousDocs());
         assertEquals(SplitDocType.DEFAULT, prevDoc.getSplitDocType());
@@ -135,7 +134,7 @@ public class DocumentSplitTest extends B
                     || doc.getCommitRootPath(rev) != null);
             assertTrue(doc.isCommitted(rev));
         }
-        DocumentNodeState node = ns.getNode("/foo", Revision.fromString(head));
+        DocumentNodeState node = ns.getNode("/foo", RevisionVector.fromString(head));
         // check status of node
         if (create) {
             assertNull(node);
@@ -290,7 +289,7 @@ public class DocumentSplitTest extends B
             // read current value
             NodeDocument doc = ds.find(NODES, Utils.getIdFromPath("/test"));
             assertNotNull(doc);
-            Revision head = ns.getHeadRevision();
+            RevisionVector head = ns.getHeadRevision();
             Revision lastRev = ns.getPendingModifications().get("/test");
             DocumentNodeState n = doc.getNodeAtRevision(mk.getNodeStore(), head, lastRev);
             assertNotNull(n);
@@ -326,7 +325,7 @@ public class DocumentSplitTest extends B
         doc = store.find(NODES, Utils.getIdFromPath("/test/foo"));
         assertNotNull(doc);
         DocumentNodeState node = doc.getNodeAtRevision(ns,
-                Revision.fromString(rev), null);
+                RevisionVector.fromString(rev), null);
         assertNotNull(node);
     }
 
@@ -473,7 +472,7 @@ public class DocumentSplitTest extends B
         Map<Revision, String> valueMap = doc.getValueMap("prop");
         for (Map.Entry<Revision, String> entry : valueMap.entrySet()) {
             if (previous != null) {
-                assertTrue(ns.isRevisionNewer(previous, entry.getKey()));
+                assertTrue(previous.compareRevisionTime(entry.getKey()) > 0);
             }
             previous = entry.getKey();
             numValues++;
@@ -482,7 +481,7 @@ public class DocumentSplitTest extends B
         assertEquals(revs.size(), numValues);
         assertEquals(revs.size(), valueMap.size());
 
-        assertNotNull(doc.getNodeAtRevision(ns, Revision.fromString(rev), null));
+        assertNotNull(doc.getNodeAtRevision(ns, RevisionVector.fromString(rev), null));
     }
 
     @Test
@@ -525,7 +524,7 @@ public class DocumentSplitTest extends B
         }
         // some fake previous doc references to trigger UpdateOp
         // for an intermediate document
-        TreeSet<Revision> prev = Sets.newTreeSet(mk.getNodeStore().getRevisionComparator());
+        TreeSet<Revision> prev = Sets.newTreeSet(StableRevisionComparator.INSTANCE);
         for (int i = 0; i < PREV_SPLIT_FACTOR; i++) {
             Revision low = Revision.newRevision(clusterId);
             Revision high = Revision.newRevision(clusterId);
@@ -669,7 +668,7 @@ public class DocumentSplitTest extends B
         NodeDocument doc = new NodeDocument(mk.getDocumentStore());
         doc.put(NodeDocument.ID, Utils.getIdFromPath("/test"));
         doc.put(NodeDocument.SD_TYPE, NodeDocument.SplitDocType.DEFAULT.type);
-        Revision head = mk.getNodeStore().getHeadRevision();
+        RevisionVector head = mk.getNodeStore().getHeadRevision();
         SplitOperations.forDocument(doc, DummyRevisionContext.INSTANCE, head, NUM_REVS_THRESHOLD);
     }
 
@@ -780,7 +779,7 @@ public class DocumentSplitTest extends B
         final DocumentStore store = mk.getDocumentStore();
         final DocumentNodeStore ns = mk.getNodeStore();
         final List<Exception> exceptions = Lists.newArrayList();
-        final List<Revision> revisions = Lists.newArrayList();
+        final List<RevisionVector> revisions = Lists.newArrayList();
 
         Thread t = new Thread(new Runnable() {
             @Override
@@ -811,7 +810,7 @@ public class DocumentSplitTest extends B
         RevisionContext rc = new TestRevisionContext(ns);
         while (t.isAlive()) {
             for (String id : ns.getSplitCandidates()) {
-                Revision head = ns.getHeadRevision();
+                RevisionVector head = ns.getHeadRevision();
                 NodeDocument doc = store.find(NODES, id);
                 List<UpdateOp> ops = SplitOperations.forDocument(doc, rc, head, NUM_REVS_THRESHOLD);
                 Set<Revision> removed = Sets.newHashSet();
@@ -842,7 +841,10 @@ public class DocumentSplitTest extends B
             if (doc.isSplitDocument() || Utils.getDepthFromId(doc.getId()) < 2) {
                 continue;
             }
-            Set<Revision> revs = Sets.newHashSet(revisions);
+            Set<Revision> revs = Sets.newHashSet();
+            for (RevisionVector rv : revisions) {
+                Iterables.addAll(revs, rv);
+            }
             revs.removeAll(doc.getValueMap("_deleted").keySet());
             assertTrue("Missing _deleted entries on " + doc.getId() + ": " + revs, revs.isEmpty());
         }
@@ -867,18 +869,13 @@ public class DocumentSplitTest extends B
         }
 
         @Override
-        public Comparator<Revision> getRevisionComparator() {
-            return rc.getRevisionComparator();
-        }
-
-        @Override
         public int getClusterId() {
             return rc.getClusterId();
         }
 
         @Nonnull
         @Override
-        public Revision getHeadRevision() {
+        public RevisionVector getHeadRevision() {
             try {
                 Thread.sleep((long) (Math.random() * 100));
             } catch (InterruptedException e) {

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DummyRevisionContext.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DummyRevisionContext.java?rev=1723532&r1=1723531&r2=1723532&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DummyRevisionContext.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DummyRevisionContext.java Thu Jan  7 12:46:35 2016
@@ -16,8 +16,6 @@
  */
 package org.apache.jackrabbit.oak.plugins.document;
 
-import java.util.Comparator;
-
 import javax.annotation.Nonnull;
 
 /**
@@ -27,12 +25,9 @@ public class DummyRevisionContext implem
 
     static final RevisionContext INSTANCE = new DummyRevisionContext();
 
-    private final Comparator<Revision> comparator
-            = StableRevisionComparator.INSTANCE;
-
     @Override
     public UnmergedBranches getBranches() {
-        return new UnmergedBranches(comparator);
+        return new UnmergedBranches();
     }
 
     @Override
@@ -41,24 +36,19 @@ public class DummyRevisionContext implem
     }
 
     @Override
-    public Comparator<Revision> getRevisionComparator() {
-        return comparator;
-    }
-
-    @Override
     public int getClusterId() {
         return 1;
     }
 
     @Nonnull
     @Override
-    public Revision getHeadRevision() {
-        return Revision.newRevision(1);
+    public RevisionVector getHeadRevision() {
+        return new RevisionVector(Revision.newRevision(getClusterId()));
     }
 
     @Nonnull
     @Override
     public Revision newRevision() {
-        return Revision.newRevision(1);
+        return Revision.newRevision(getClusterId());
     }
 }

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/JournalEntryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/JournalEntryTest.java?rev=1723532&r1=1723531&r2=1723532&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/JournalEntryTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/JournalEntryTest.java Thu Jan  7 12:46:35 2016
@@ -53,8 +53,8 @@ public class JournalEntryTest {
         addRandomPaths(paths);
         StringSort sort = JournalEntry.newSorter();
         add(sort, paths);
-        Revision from = new Revision(1, 0, 1);
-        Revision to = new Revision(2, 0, 1);
+        RevisionVector from = new RevisionVector(new Revision(1, 0, 1));
+        RevisionVector to = new RevisionVector(new Revision(2, 0, 1));
         sort.sort();
         JournalEntry.applyTo(sort, cache, from, to);
 
@@ -72,9 +72,9 @@ public class JournalEntryTest {
     @Test
     public void useParentDiff() throws Exception {
         DiffCache cache = new MemoryDiffCache(new DocumentMK.Builder());
-        Revision from = new Revision(1, 0, 1);
-        Revision to = new Revision(2, 0, 1);
-        Revision unjournalled = new Revision(3, 0, 1);
+        RevisionVector from = new RevisionVector(new Revision(1, 0, 1));
+        RevisionVector to = new RevisionVector(new Revision(2, 0, 1));
+        RevisionVector unjournalled = new RevisionVector(new Revision(3, 0, 1));
 
         //Put one entry for (from, to, "/a/b")->["c1", "c2"] manually
         DiffCache.Entry entry = cache.newEntry(from, to, false);
@@ -190,7 +190,11 @@ public class JournalEntryTest {
         }
     }
 
-    private void validateCacheUsage(DiffCache cache, Revision from, Revision to, String path, boolean cacheExpected) {
+    private void validateCacheUsage(DiffCache cache,
+                                    RevisionVector from,
+                                    RevisionVector to,
+                                    String path,
+                                    boolean cacheExpected) {
         String nonLoaderDiff = cache.getChanges(from, to, path, null);
         final AtomicBoolean loaderCalled = new AtomicBoolean(false);
         cache.getChanges(from, to, path, new DiffCache.Loader() {

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/JournalTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/JournalTest.java?rev=1723532&r1=1723531&r2=1723532&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/JournalTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/JournalTest.java Thu Jan  7 12:46:35 2016
@@ -41,6 +41,7 @@ import org.junit.rules.TestRule;
 
 import static java.util.Collections.synchronizedList;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
@@ -371,7 +372,7 @@ public class JournalTest extends Abstrac
         Revision zlastRev2 = z1.getLastRev().get(c2Id);
         // /x/y/z is a new node and does not have a _lastRev
         assertNull(zlastRev2);
-        Revision head2 = ds2.getHeadRevision();
+        Revision head2 = ds2.getHeadRevision().getRevision(ds2.getClusterId());
 
         //lastRev should not be updated for C #2
         assertNull(y1.getLastRev().get(c2Id));
@@ -458,7 +459,8 @@ public class JournalTest extends Abstrac
         NodeBuilder b2 = ns2.getRoot().builder();
         b2.child("bar");
         ns2.merge(b2, EmptyHook.INSTANCE, CommitInfo.EMPTY);
-        Revision h2 = ns2.getHeadRevision();
+        Revision h2 = ns2.getHeadRevision().getRevision(ns2.getClusterId());
+        assertNotNull(h2);
 
         ns2.runBackgroundReadOperations();
 

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/LastRevRecoveryAgentTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/LastRevRecoveryAgentTest.java?rev=1723532&r1=1723531&r2=1723532&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/LastRevRecoveryAgentTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/LastRevRecoveryAgentTest.java Thu Jan  7 12:46:35 2016
@@ -139,7 +139,7 @@ public class LastRevRecoveryAgentTest {
         b2.child("x").child("y").child("z").setProperty("foo", "bar");
         ds2.merge(b2, EmptyHook.INSTANCE, CommitInfo.EMPTY);
 
-        Revision zlastRev2 = ds2.getHeadRevision();
+        Revision zlastRev2 = ds2.getHeadRevision().getRevision(ds2.getClusterId());
 
         long leaseTime = ds1.getClusterInfo().getLeaseTime();
         ds1.runBackgroundOperations();

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/LastRevRecoveryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/LastRevRecoveryTest.java?rev=1723532&r1=1723531&r2=1723532&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/LastRevRecoveryTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/LastRevRecoveryTest.java Thu Jan  7 12:46:35 2016
@@ -122,7 +122,7 @@ public class LastRevRecoveryTest {
         Revision zlastRev2 = z1.getLastRev().get(c2Id);
         // /x/y/z is a new node and does not have a _lastRev
         assertNull(zlastRev2);
-        Revision head2 = ds2.getHeadRevision();
+        Revision head2 = ds2.getHeadRevision().getRevision(c2Id);
 
         //lastRev should not be updated for C #2
         assertNull(y1.getLastRev().get(c2Id));

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/MeasureMemory.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/MeasureMemory.java?rev=1723532&r1=1723531&r2=1723532&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/MeasureMemory.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/MeasureMemory.java Thu Jan  7 12:46:35 2016
@@ -167,12 +167,50 @@ public class MeasureMemory {
             @Override
             public Object[] call() {
                 RevisionsKey k = new RevisionsKey(
-                        Revision.newRevision(0), Revision.newRevision(0));
+                        new RevisionVector(Revision.newRevision(0)),
+                        new RevisionVector(Revision.newRevision(0)));
                 return new Object[]{k, k.getMemory() + OVERHEAD};
             }
         });
     }
 
+    @Test
+    public void revisionVector() throws Exception {
+        measureMemory(new Callable<Object[]>() {
+            @Override
+            public Object[] call() throws Exception {
+                RevisionVector rv = new RevisionVector(
+                        Revision.newRevision(0),
+                        Revision.newRevision(1),
+                        Revision.newRevision(2),
+                        Revision.newRevision(3));
+                return new Object[]{rv, rv.getMemory() + OVERHEAD};
+            }
+        });
+    }
+
+    @Test
+    public void revisionVectorSingle() throws Exception {
+        measureMemory(new Callable<Object[]>() {
+            @Override
+            public Object[] call() throws Exception {
+                RevisionVector rv = new RevisionVector(Revision.newRevision(0));
+                return new Object[]{rv, rv.getMemory() + OVERHEAD};
+            }
+        });
+    }
+
+    @Test
+    public void revision() throws Exception {
+        measureMemory(new Callable<Object[]>() {
+            @Override
+            public Object[] call() throws Exception {
+                Revision r = Revision.newRevision(0);
+                return new Object[]{r, r.getMemory() + OVERHEAD};
+            }
+        });
+    }
+
     private static void measureMemory(Callable<Object[]> c) throws Exception {
         LinkedList<Object> list = new LinkedList<Object>();
         long base = getMemoryUsed();
@@ -201,11 +239,11 @@ public class MeasureMemory {
 
     static DocumentNodeState generateNode(int propertyCount) {
         DocumentNodeState n = new DocumentNodeState(STORE, new String("/hello/world"),
-                new Revision(1, 2, 3));
+                new RevisionVector(new Revision(1, 2, 3)));
         for (int i = 0; i < propertyCount; i++) {
             n.setProperty("property" + i, "\"values " + i + "\"");
         }
-        n.setLastRevision(new Revision(1, 2, 3));
+        n.setLastRevision(new RevisionVector(new Revision(1, 2, 3)));
         return n;
     }
 

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/MongoDocumentStoreTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/MongoDocumentStoreTest.java?rev=1723532&r1=1723531&r2=1723532&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/MongoDocumentStoreTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/MongoDocumentStoreTest.java Thu Jan  7 12:46:35 2016
@@ -228,8 +228,9 @@ public class MongoDocumentStoreTest {
         Revision rev = Revision.newRevision(0);
         List<UpdateOp> inserts = new ArrayList<UpdateOp>();
         for (int i = 0; i < DocumentMK.MANY_CHILDREN_THRESHOLD * 2; i++) {
-            DocumentNodeState n = new DocumentNodeState(store, "/node-" + i, rev);
-            inserts.add(n.asOperation(true));
+            DocumentNodeState n = new DocumentNodeState(store, "/node-" + i,
+                    new RevisionVector(rev));
+            inserts.add(n.asOperation(rev));
         }
         docStore.create(Collection.NODES, inserts);
         List<NodeDocument> docs = docStore.query(Collection.NODES,

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentTest.java?rev=1723532&r1=1723531&r2=1723532&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentTest.java Thu Jan  7 12:46:35 2016
@@ -17,7 +17,6 @@
 package org.apache.jackrabbit.oak.plugins.document;
 
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -28,7 +27,6 @@ import java.util.Set;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Iterators;
 import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
 
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.VersionGCStats;
@@ -40,10 +38,9 @@ import org.apache.jackrabbit.oak.spi.sta
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
 import org.junit.Test;
 
+import static com.google.common.collect.Sets.newHashSet;
 import static org.apache.jackrabbit.oak.plugins.document.Collection.NODES;
 import static org.apache.jackrabbit.oak.plugins.document.NodeDocument.COLLISIONS;
-import static org.apache.jackrabbit.oak.plugins.document.NodeDocument.revisionAreAmbiguous;
-import static org.apache.jackrabbit.oak.plugins.document.Revision.RevisionComparator;
 import static org.apache.jackrabbit.oak.plugins.document.util.Utils.getRootDocument;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -70,58 +67,20 @@ public class NodeDocumentTest {
             NodeDocument.addCollision(op, r, Revision.newRevision(1));
         }
         UpdateUtils.applyChanges(doc, op);
-        Revision head = DummyRevisionContext.INSTANCE.getHeadRevision();
+        RevisionVector head = DummyRevisionContext.INSTANCE.getHeadRevision();
         doc.split(DummyRevisionContext.INSTANCE, head);
     }
 
     @Test
-    public void ambiguousRevisions() {
-        // revisions from same cluster node are not ambiguous
-        RevisionContext context = DummyRevisionContext.INSTANCE;
-        Revision r1 = new Revision(1, 0, 1);
-        Revision r2 = new Revision(2, 0, 1);
-        assertFalse(revisionAreAmbiguous(context, r1, r1));
-        assertFalse(revisionAreAmbiguous(context, r1, r2));
-        assertFalse(revisionAreAmbiguous(context, r2, r1));
-
-        // revisions from different cluster nodes are not ambiguous
-        // if seen with stable revision comparator
-        r1 = new Revision(1, 0, 2);
-        r2 = new Revision(2, 0, 1);
-        assertFalse(revisionAreAmbiguous(context, r1, r1));
-        assertFalse(revisionAreAmbiguous(context, r1, r2));
-        assertFalse(revisionAreAmbiguous(context, r2, r1));
-
-        // now use a revision comparator with seen-at support
-        final RevisionComparator comparator = new RevisionComparator(1);
-        context = new DummyRevisionContext() {
-            @Override
-            public Comparator<Revision> getRevisionComparator() {
-                return comparator;
-            }
-        };
-        r1 = new Revision(1, 0, 2);
-        r2 = new Revision(2, 0, 1);
-        // add revision to comparator in reverse time order
-        comparator.add(r2, new Revision(2, 0, 0));
-        comparator.add(r1, new Revision(3, 0, 0)); // r1 seen after r2
-        assertFalse(revisionAreAmbiguous(context, r1, r1));
-        assertFalse(revisionAreAmbiguous(context, r2, r2));
-        assertTrue(revisionAreAmbiguous(context, r1, r2));
-        assertTrue(revisionAreAmbiguous(context, r2, r1));
-    }
-
-    @Test
-    public void getMostRecentConflictFor() {
-        RevisionContext context = DummyRevisionContext.INSTANCE;
+    public void getConflictsFor() {
         MemoryDocumentStore docStore = new MemoryDocumentStore();
         String id = Utils.getPathFromId("/");
         NodeDocument doc = new NodeDocument(docStore);
         doc.put(Document.ID, id);
 
         Iterable<Revision> branchCommits = Collections.emptyList();
-        Revision conflict = doc.getMostRecentConflictFor(branchCommits, context);
-        assertNull(conflict);
+        Set<Revision> conflicts = doc.getConflictsFor(branchCommits);
+        assertTrue(conflicts.isEmpty());
 
         // add some collisions
         UpdateOp op = new UpdateOp(id, false);
@@ -138,24 +97,24 @@ public class NodeDocumentTest {
         UpdateUtils.applyChanges(doc, op);
 
         branchCommits = Collections.singleton(r0);
-        conflict = doc.getMostRecentConflictFor(branchCommits, context);
-        assertNull(conflict);
+        conflicts = doc.getConflictsFor(branchCommits);
+        assertTrue(conflicts.isEmpty());
 
         branchCommits = Collections.singleton(r1.asBranchRevision());
-        conflict = doc.getMostRecentConflictFor(branchCommits, context);
-        assertEquals(c1, conflict);
+        conflicts = doc.getConflictsFor(branchCommits);
+        assertEquals(newHashSet(c1), conflicts);
 
         branchCommits = Collections.singleton(r2.asBranchRevision());
-        conflict = doc.getMostRecentConflictFor(branchCommits, context);
-        assertEquals(c2, conflict);
+        conflicts = doc.getConflictsFor(branchCommits);
+        assertEquals(newHashSet(c2), conflicts);
 
         branchCommits = Lists.newArrayList(r1.asBranchRevision(), r2.asBranchRevision());
-        conflict = doc.getMostRecentConflictFor(branchCommits, context);
-        assertEquals(c2, conflict);
+        conflicts = doc.getConflictsFor(branchCommits);
+        assertEquals(newHashSet(c1, c2), conflicts);
 
         branchCommits = Lists.newArrayList(r2.asBranchRevision(), r1.asBranchRevision());
-        conflict = doc.getMostRecentConflictFor(branchCommits, context);
-        assertEquals(c2, conflict);
+        conflicts = doc.getConflictsFor(branchCommits);
+        assertEquals(newHashSet(c1, c2), conflicts);
     }
 
     @Test
@@ -240,7 +199,7 @@ public class NodeDocumentTest {
             builder.setProperty("p-" + clusterIdx, i);
             merge(ns, builder);
             if (r.nextFloat() < 0.2) {
-                Revision head = ns.getHeadRevision();
+                RevisionVector head = ns.getHeadRevision();
                 for (UpdateOp op : SplitOperations.forDocument(
                         getRootDocument(store), ns, head, 2)) {
                     store.createOrUpdate(NODES, op);
@@ -328,7 +287,7 @@ public class NodeDocumentTest {
     @Test
     public void getNewestRevisionTooExpensive() throws Exception {
         final int NUM_CHANGES = 200;
-        final Set<String> prevDocCalls = Sets.newHashSet();
+        final Set<String> prevDocCalls = newHashSet();
         DocumentStore store = new MemoryDocumentStore() {
             @Override
             public <T extends Document> T find(Collection<T> collection,
@@ -354,7 +313,7 @@ public class NodeDocumentTest {
             }
             merge(ns, builder);
             if (Math.random() < 0.2) {
-                Revision head = ns.getHeadRevision();
+                RevisionVector head = ns.getHeadRevision();
                 NodeDocument doc = ns.getDocumentStore().find(
                         NODES, Utils.getIdFromPath("/test"));
                 for (UpdateOp op : SplitOperations.forDocument(
@@ -374,7 +333,8 @@ public class NodeDocumentTest {
         Revision changeRev = new Revision(baseRev.getTimestamp(), 1000, ns.getClusterId());
         // reset calls to previous documents
         prevDocCalls.clear();
-        doc.getNewestRevision(ns, baseRev, changeRev, null, new HashSet<Revision>());
+        doc.getNewestRevision(ns, new RevisionVector(baseRev), changeRev,
+                null, new HashSet<Revision>());
         // must not read all previous docs
         assertTrue("too many calls for previous documents: " + prevDocCalls,
                 prevDocCalls.size() <= 5);
@@ -391,10 +351,11 @@ public class NodeDocumentTest {
         NodeBuilder b1 = ns1.getRoot().builder();
         b1.child("test");
         merge(ns1, b1);
-        Revision created = ns1.getHeadRevision();
+        RevisionVector headCreated = ns1.getHeadRevision();
+        Revision created = headCreated.getRevision(ns1.getClusterId());
 
         NodeDocument doc = store.find(NODES, Utils.getIdFromPath("/test"));
-        Set<Revision> collisions = Sets.newHashSet();
+        Set<Revision> collisions = newHashSet();
         Revision newest = doc.getNewestRevision(ns1, ns1.getHeadRevision(),
                 ns1.newRevision(), null, collisions);
         assertEquals(created, newest);
@@ -444,14 +405,14 @@ public class NodeDocumentTest {
         b1 = ns1.getRoot().builder();
         b1.child("test").setProperty("q", "v");
         merge(ns1, b1);
-        Revision committed = ns1.getHeadRevision();
+        Revision committed = ns1.getHeadRevision().getRevision(ns1.getClusterId());
 
         collisions.clear();
         // ns1 must now report committed revision as newest
         // uncommitted is not considered a collision anymore
         // because it is older than the base revision
         doc = store.find(NODES, Utils.getIdFromPath("/test"));
-        newest = doc.getNewestRevision(ns1, created,
+        newest = doc.getNewestRevision(ns1, headCreated,
                 ns1.newRevision(), null, collisions);
         assertEquals(committed, newest);
         assertEquals(0, collisions.size());
@@ -479,9 +440,9 @@ public class NodeDocumentTest {
         builder.child("test");
         merge(ns, builder);
 
-        Set<Revision> collisions = Sets.newHashSet();
+        Set<Revision> collisions = newHashSet();
         NodeDocument doc = store.find(NODES, Utils.getIdFromPath("/test"));
-        Revision branchBase = ns.getHeadRevision().asBranchRevision();
+        RevisionVector branchBase = ns.getHeadRevision().asBranchRevision(ns.getClusterId());
         try {
             doc.getNewestRevision(ns, branchBase, ns.newRevision(), null, collisions);
             fail("Must fail with IllegalArgumentException");
@@ -489,7 +450,7 @@ public class NodeDocumentTest {
             // expected
         }
         try {
-            Revision head = ns.getHeadRevision();
+            RevisionVector head = ns.getHeadRevision();
             Branch b = ns.getBranches().create(head, ns.newRevision(), null);
             doc.getNewestRevision(ns, head, ns.newRevision(), b, collisions);
             fail("Must fail with IllegalArgumentException");
@@ -510,7 +471,7 @@ public class NodeDocumentTest {
         for (int i = 0; i < 10; i++) {
             int idx = random.nextInt(numChanges);
             Revision r = Iterables.get(doc.getValueMap("p").keySet(), idx);
-            Iterable<Revision> revs = doc.getChanges("p", r, ns);
+            Iterable<Revision> revs = doc.getChanges("p", new RevisionVector(r));
             assertEquals(idx, Iterables.size(revs));
         }
         ns.dispose();
@@ -525,15 +486,17 @@ public class NodeDocumentTest {
         DocumentNodeStore ns2 = createTestStore(store, 2, 0);
         List<DocumentNodeStore> nodeStores = Lists.newArrayList(ns1, ns2);
 
+        List<RevisionVector> headRevisions = Lists.newArrayList();
         for (int i = 0; i < numChanges; i++) {
             DocumentNodeStore ns = nodeStores.get(random.nextInt(nodeStores.size()));
             ns.runBackgroundOperations();
             NodeBuilder builder = ns.getRoot().builder();
             builder.setProperty("p", i);
             merge(ns, builder);
+            headRevisions.add(ns.getHeadRevision());
             ns.runBackgroundOperations();
             if (random.nextDouble() < 0.2) {
-                Revision head = ns.getHeadRevision();
+                RevisionVector head = ns.getHeadRevision();
                 for (UpdateOp op : SplitOperations.forDocument(
                         getRootDocument(store), ns, head, 2)) {
                     store.createOrUpdate(NODES, op);
@@ -541,12 +504,13 @@ public class NodeDocumentTest {
             }
         }
 
+        headRevisions = Lists.reverse(headRevisions);
         NodeDocument doc = getRootDocument(store);
         for (int i = 0; i < 10; i++) {
             int idx = random.nextInt(numChanges);
-            Revision r = Iterables.get(doc.getValueMap("p").keySet(), idx);
-            Iterable<Revision> revs1 = doc.getChanges("p", r, ns1);
-            Iterable<Revision> revs2 = doc.getChanges("p", r, ns2);
+            RevisionVector r = headRevisions.get(idx);
+            Iterable<Revision> revs1 = doc.getChanges("p", r);
+            Iterable<Revision> revs2 = doc.getChanges("p", r);
             assertEquals(Iterables.size(revs1), Iterables.size(revs2));
             assertEquals(idx, Iterables.size(revs1));
         }
@@ -559,7 +523,7 @@ public class NodeDocumentTest {
     @Test
     public void isConflicting() throws Exception {
         final int numChanges = 200;
-        final Set<String> prevDocCalls = Sets.newHashSet();
+        final Set<String> prevDocCalls = newHashSet();
         MemoryDocumentStore store = new MemoryDocumentStore() {
             @Override
             public <T extends Document> T find(Collection<T> collection,
@@ -574,13 +538,13 @@ public class NodeDocumentTest {
         NodeDocument doc = getRootDocument(store);
         Map<Revision, String> valueMap = doc.getValueMap("p");
         assertEquals(200, valueMap.size());
-        Revision baseRev = valueMap.keySet().iterator().next();
+        RevisionVector baseRev = new RevisionVector(valueMap.keySet().iterator().next());
         Revision commitRev = ns.newRevision();
         UpdateOp op = new UpdateOp(Utils.getIdFromPath("/"), false);
         op.setMapEntry("p", commitRev, "v");
 
         prevDocCalls.clear();
-        assertFalse(doc.isConflicting(op, baseRev, commitRev, ns, false));
+        assertFalse(doc.isConflicting(op, baseRev, commitRev, false));
         assertTrue("too many calls for previous documents: " + prevDocCalls,
                 prevDocCalls.size() <= 6);
         ns.dispose();
@@ -601,7 +565,7 @@ public class NodeDocumentTest {
             builder.setProperty("p", i);
             merge(ns, builder);
             if (Math.random() < 0.2) {
-                Revision head = ns.getHeadRevision();
+                RevisionVector head = ns.getHeadRevision();
                 for (UpdateOp op : SplitOperations.forDocument(
                         getRootDocument(store), ns, head, 2)) {
                     store.createOrUpdate(NODES, op);