Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java?rev=1723532&r1=1723531&r2=1723532&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java Thu Jan 7 12:46:35 2016 @@ -42,17 +42,14 @@ import java.lang.ref.WeakReference; import java.text.SimpleDateFormat; import java.util.Collections; import java.util.Date; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NavigableSet; import java.util.Set; -import java.util.SortedMap; import java.util.TimeZone; import java.util.concurrent.Callable; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; @@ -68,6 +65,7 @@ import javax.annotation.Nullable; import javax.management.NotCompliantMBeanException; import com.google.common.base.Function; +import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; @@ -83,7 +81,6 @@ import org.apache.jackrabbit.oak.commons import org.apache.jackrabbit.oak.plugins.blob.BlobStoreBlob; import org.apache.jackrabbit.oak.plugins.blob.MarkSweepGarbageCollector; import org.apache.jackrabbit.oak.plugins.blob.ReferencedBlob; -import org.apache.jackrabbit.oak.plugins.document.Checkpoints.Info; import org.apache.jackrabbit.oak.plugins.document.cache.CacheInvalidationStats; import org.apache.jackrabbit.oak.plugins.document.persistentCache.PersistentCache; import org.apache.jackrabbit.oak.spi.blob.BlobStore; @@ -95,7 +92,6 @@ import org.apache.jackrabbit.oak.api.Com import org.apache.jackrabbit.oak.cache.CacheStats; import org.apache.jackrabbit.oak.commons.PathUtils; import org.apache.jackrabbit.oak.json.BlobSerializer; -import org.apache.jackrabbit.oak.plugins.document.Branch.BranchCommit; import org.apache.jackrabbit.oak.plugins.document.util.LeaseCheckDocumentStoreWrapper; import org.apache.jackrabbit.oak.plugins.document.util.LoggingDocumentStoreWrapper; import org.apache.jackrabbit.oak.plugins.document.util.StringValue; @@ -135,13 +131,6 @@ public final class DocumentNodeStore static final int NUM_CHILDREN_CACHE_LIMIT = Integer.getInteger("oak.documentMK.childrenCacheLimit", 16 * 1024); /** - * When trying to access revisions that are older than this many - * milliseconds, a warning is logged. The default is one minute. - */ - private static final int WARN_REVISION_AGE = - Integer.getInteger("oak.documentMK.revisionAge", 60 * 1000); - - /** * Feature flag to enable concurrent add/remove operations of hidden empty * nodes. See OAK-2673. */ @@ -155,12 +144,6 @@ public final class DocumentNodeStore Boolean.parseBoolean(System.getProperty("oak.fairBackgroundOperationLock", "true")); /** - * How long to remember the relative order of old revision of all cluster - * nodes, in milliseconds. The default is one hour. - */ - static final int REMEMBER_REVISION_ORDER_MILLIS = 60 * 60 * 1000; - - /** * The document store (might be used by multiple node stores). */ protected final DocumentStore store; @@ -214,24 +197,12 @@ public final class DocumentNodeStore private final int clusterId; /** - * Map of inactive cluster nodes and when the cluster node was last seen - * as inactive. - * Key: clusterId, value: timeInMillis + * Map of known cluster nodes and the last known state updated + * by {@link #updateClusterState()}. + * Key: clusterId, value: ClusterNodeInfoDocument */ - private final ConcurrentMap<Integer, Long> inactiveClusterNodes - = new ConcurrentHashMap<Integer, Long>(); - - /** - * Map of active cluster nodes and when the cluster node's lease ends. - * Key: clusterId, value: leaseEndTimeInMillis - */ - private final ConcurrentMap<Integer, Long> activeClusterNodes - = new ConcurrentHashMap<Integer, Long>(); - - /** - * The comparator for revisions. - */ - private final Revision.RevisionComparator revisionComparator; + private final ConcurrentMap<Integer, ClusterNodeInfoDocument> clusterNodes + = Maps.newConcurrentMap(); /** * Unmerged branches of this DocumentNodeStore instance. @@ -261,17 +232,9 @@ public final class DocumentNodeStore private JournalEntry changes; /** - * The last known revision for each cluster instance. - * - * Key: the machine id, value: revision. + * The current root node state. */ - private final Map<Integer, Revision> lastKnownRevision = - new ConcurrentHashMap<Integer, Revision>(); - - /** - * The last known head revision. This is the last-known revision. - */ - private volatile Revision headRevision; + private volatile DocumentNodeState root; private Thread backgroundReadThread; @@ -444,8 +407,7 @@ public final class DocumentNodeStore } this.store = s; this.clusterId = cid; - this.revisionComparator = new Revision.RevisionComparator(clusterId); - this.branches = new UnmergedBranches(getRevisionComparator()); + this.branches = new UnmergedBranches(); this.asyncDelay = builder.getAsyncDelay(); this.versionGarbageCollector = new VersionGarbageCollector( this, builder.createVersionGCSupport()); @@ -454,7 +416,8 @@ public final class DocumentNodeStore this.lastRevRecoveryAgent = new LastRevRecoveryAgent(this, builder.createMissingLastRevSeeker()); this.disableBranches = builder.isDisableBranches(); - this.missing = new DocumentNodeState(this, "MISSING", new Revision(0, 0, 0)) { + this.missing = new DocumentNodeState(this, "MISSING", + new RevisionVector(new Revision(0, 0, 0))) { @Override public int getMemory() { return 8; @@ -482,14 +445,16 @@ public final class DocumentNodeStore NodeDocument rootDoc = store.find(NODES, Utils.getIdFromPath("/")); if (rootDoc == null) { // root node is missing: repository is not initialized - Revision head = newRevision(); - Commit commit = new Commit(this, head, null, null); + Revision commitRev = newRevision(); + Commit commit = new Commit(this, commitRev, null, null); + RevisionVector head = new RevisionVector(commitRev); DocumentNodeState n = new DocumentNodeState(this, "/", head); commit.addNode(n); commit.applyToDocumentStore(); // use dummy Revision as before - commit.applyToCache(new Revision(0, 0, clusterId), false); - setHeadRevision(commit.getRevision()); + RevisionVector before = new RevisionVector(new Revision(0, 0, clusterId)); + commit.applyToCache(before, false); + setRoot(head); // make sure _lastRev is written back to store backgroundWrite(); rootDoc = store.find(NODES, Utils.getIdFromPath("/")); @@ -499,10 +464,10 @@ public final class DocumentNodeStore } } else { checkLastRevRecovery(); - initializeHeadRevision(rootDoc); + initializeRootState(rootDoc); // check if _lastRev for our clusterId exists if (!rootDoc.getLastRev().containsKey(clusterId)) { - unsavedLastRevisions.put("/", headRevision); + unsavedLastRevisions.put("/", getRoot().getRevision().getRevision(clusterId)); backgroundWrite(); } } @@ -510,15 +475,13 @@ public final class DocumentNodeStore // Renew the lease because it may have been stale renewClusterIdLease(); - getRevisionComparator().add(headRevision, Revision.newRevision(0)); - // initialize branchCommits branches.init(store, this); dispatcher = new ChangeDispatcher(getRoot()); commitQueue = new CommitQueue(this); String threadNamePostfix = "(" + clusterId + ")"; - batchCommitQueue = new BatchCommitQueue(store, revisionComparator); + batchCommitQueue = new BatchCommitQueue(store); backgroundReadThread = new Thread( new BackgroundReadOperation(this, isDisposed), "DocumentNodeStore background read thread " + threadNamePostfix); @@ -617,14 +580,9 @@ public final class DocumentNodeStore return clusterNodeInfo.toString().replaceAll("[\r\n\t]", " ").trim(); } - Revision setHeadRevision(@Nonnull Revision newHead) { + void setRoot(@Nonnull RevisionVector newHead) { checkArgument(!newHead.isBranch()); - Revision previous = headRevision; - if (!checkNotNull(newHead).equals(previous)) { - // head changed - headRevision = newHead; - } - return previous; + root = getRoot(newHead); } @Nonnull @@ -647,10 +605,10 @@ public final class DocumentNodeStore * @return a new commit. */ @Nonnull - Commit newCommit(@Nullable Revision base, + Commit newCommit(@Nullable RevisionVector base, @Nullable DocumentNodeStoreBranch branch) { if (base == null) { - base = headRevision; + base = getHeadRevision(); } if (base.isBranch()) { return newBranchCommit(base, branch); @@ -670,9 +628,9 @@ public final class DocumentNodeStore * @return a new merge commit. */ @Nonnull - MergeCommit newMergeCommit(@Nullable Revision base, int numBranchCommits) { + MergeCommit newMergeCommit(@Nullable RevisionVector base, int numBranchCommits) { if (base == null) { - base = headRevision; + base = getHeadRevision(); } backgroundOperationLock.readLock().lock(); boolean success = false; @@ -689,30 +647,34 @@ public final class DocumentNodeStore return c; } - void done(final @Nonnull Commit c, boolean isBranch, final @Nullable CommitInfo info) { + RevisionVector done(final @Nonnull Commit c, boolean isBranch, final @Nullable CommitInfo info) { if (commitQueue.contains(c.getRevision())) { try { + final RevisionVector[] newHead = new RevisionVector[1]; commitQueue.done(c.getRevision(), new CommitQueue.Callback() { @Override public void headOfQueue(@Nonnull Revision revision) { // remember before revision - Revision before = getHeadRevision(); + RevisionVector before = getHeadRevision(); // apply changes to cache based on before revision c.applyToCache(before, false); // track modified paths changes.modified(c.getModifiedPaths()); // update head revision - setHeadRevision(c.getRevision()); + newHead[0] = before.update(c.getRevision()); + setRoot(newHead[0]); commitQueue.headRevisionChanged(); dispatcher.contentChanged(getRoot(), info); } }); + return newHead[0]; } finally { backgroundOperationLock.readLock().unlock(); } } else { // branch commit c.applyToCache(c.getBaseRevision(), isBranch); + return c.getBaseRevision().update(c.getRevision().asBranchRevision()); } } @@ -780,7 +742,7 @@ public final class DocumentNodeStore nodeChildrenCache.invalidateAll(); } - void invalidateNodeCache(String path, Revision revision){ + void invalidateNodeCache(String path, RevisionVector revision){ nodeCache.invalidate(new PathRev(path, revision)); } @@ -793,17 +755,6 @@ public final class DocumentNodeStore } /** - * 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 - */ - boolean isRevisionNewer(@Nonnull Revision x, @Nonnull Revision previous) { - return getRevisionComparator().compare(x, previous) > 0; - } - - /** * Enqueue the document with the given id as a split candidate. * * @param id the id of the document to check if it needs to be split. @@ -821,7 +772,7 @@ public final class DocumentNodeStore } void markAsDeleted(DocumentNodeState node, Commit commit, boolean subTreeAlso) { - commit.removeNode(node.getPath()); + commit.removeNode(node.getPath(), node); if (subTreeAlso) { // recurse down the tree @@ -842,8 +793,10 @@ public final class DocumentNodeStore * given revision. */ @CheckForNull - DocumentNodeState getNode(@Nonnull final String path, @Nonnull final Revision rev) { - checkRevisionAge(checkNotNull(rev), checkNotNull(path)); + DocumentNodeState getNode(@Nonnull final String path, + @Nonnull final RevisionVector rev) { + checkNotNull(rev); + checkNotNull(path); final long start = PERFLOG.start(); try { PathRev key = new PathRev(path, rev); @@ -880,7 +833,7 @@ public final class DocumentNodeStore return DocumentNodeState.NO_CHILDREN; } final String path = checkNotNull(parent).getPath(); - final Revision readRevision = parent.getLastRevision(); + final RevisionVector readRevision = parent.getLastRevision(); try { PathRev key = childNodeCacheKey(path, readRevision, name); DocumentNodeState.Children children = nodeChildrenCache.get(key, new Callable<DocumentNodeState.Children>() { @@ -925,8 +878,8 @@ public final class DocumentNodeStore String name, int limit) { String queriedName = name; String path = parent.getPath(); - Revision rev = parent.getLastRevision(); - LOG.trace("Reading children for [{}] ast rev [{}]", path, rev); + RevisionVector rev = parent.getLastRevision(); + LOG.trace("Reading children for [{}] at rev [{}]", path, rev); Iterable<NodeDocument> docs; DocumentNodeState.Children c = new DocumentNodeState.Children(); // add one to the requested limit for the raw limit @@ -1082,7 +1035,7 @@ public final class DocumentNodeStore return Collections.emptyList(); } - final Revision readRevision = parent.getLastRevision(); + final RevisionVector readRevision = parent.getLastRevision(); return transform(getChildren(parent, name, limit).children, new Function<String, DocumentNodeState>() { @Override public DocumentNodeState apply(String input) { @@ -1097,7 +1050,7 @@ public final class DocumentNodeStore } @CheckForNull - DocumentNodeState readNode(String path, Revision readRevision) { + DocumentNodeState readNode(String path, RevisionVector readRevision) { final long start = PERFLOG.start(); String id = Utils.getIdFromPath(path); Revision lastRevision = getPendingModifications().get(path); @@ -1124,7 +1077,7 @@ public final class DocumentNodeStore * @param changed the list of changed child nodes. * */ - void applyChanges(Revision rev, String path, + void applyChanges(RevisionVector rev, String path, boolean isNew, List<String> added, List<String> removed, List<String> changed, DiffCache.Entry cacheEntry) { @@ -1245,7 +1198,7 @@ public final class DocumentNodeStore * @return the root node state at the given revision. */ @Nonnull - DocumentNodeState getRoot(@Nonnull Revision revision) { + DocumentNodeState getRoot(@Nonnull RevisionVector revision) { DocumentNodeState root = getNode("/", revision); if (root == null) { throw new IllegalStateException( @@ -1264,7 +1217,8 @@ public final class DocumentNodeStore } @Nonnull - Revision rebase(@Nonnull Revision branchHead, @Nonnull Revision base) { + RevisionVector rebase(@Nonnull RevisionVector branchHead, + @Nonnull RevisionVector base) { checkNotNull(branchHead); checkNotNull(base); if (disableBranches) { @@ -1274,33 +1228,33 @@ public final class DocumentNodeStore Branch b = getBranches().getBranch(branchHead); if (b == null) { // empty branch - return base.asBranchRevision(); + return base.asBranchRevision(getClusterId()); } - if (b.getBase(branchHead).equals(base)) { + if (b.getBase(branchHead.getBranchRevision()).equals(base)) { return branchHead; } // add a pseudo commit to make sure current head of branch // has a higher revision than base of branch Revision head = newRevision().asBranchRevision(); b.rebase(head, base); - return head; + return base.update(head); } @Nonnull - Revision reset(@Nonnull Revision branchHead, - @Nonnull Revision ancestor, - @Nullable DocumentNodeStoreBranch branch) { + RevisionVector reset(@Nonnull RevisionVector branchHead, + @Nonnull RevisionVector ancestor, + @Nullable DocumentNodeStoreBranch branch) { checkNotNull(branchHead); checkNotNull(ancestor); Branch b = getBranches().getBranch(branchHead); if (b == null) { throw new DocumentStoreException("Empty branch cannot be reset"); } - if (!b.getCommits().last().equals(branchHead)) { + if (!b.getCommits().last().equals(branchHead.getRevision(getClusterId()))) { throw new DocumentStoreException(branchHead + " is not the head " + "of a branch"); } - if (!b.containsCommit(ancestor)) { + if (!b.containsCommit(ancestor.getBranchRevision())) { throw new DocumentStoreException(ancestor + " is not " + "an ancestor revision of " + branchHead); } @@ -1311,15 +1265,17 @@ public final class DocumentNodeStore boolean success = false; Commit commit = newCommit(branchHead, branch); try { - Iterator<Revision> it = b.getCommits().tailSet(ancestor).iterator(); + Iterator<Revision> it = b.getCommits().tailSet(ancestor.getBranchRevision()).iterator(); // first revision is the ancestor (tailSet is inclusive) // do not undo changes for this revision - Revision base = it.next(); + it.next(); Map<String, UpdateOp> operations = Maps.newHashMap(); - while (it.hasNext()) { + if (it.hasNext()) { Revision reset = it.next(); - getRoot(reset).compareAgainstBaseState(getRoot(base), - new ResetDiff(reset.asTrunkRevision(), operations)); + // TODO: correct? + getRoot(b.getCommit(reset).getBase().update(reset)) + .compareAgainstBaseState(getRoot(ancestor), + new ResetDiff(reset.asTrunkRevision(), operations)); UpdateOp rootOp = operations.get("/"); if (rootOp == null) { rootOp = new UpdateOp(Utils.getIdFromPath("/"), false); @@ -1333,7 +1289,7 @@ public final class DocumentNodeStore if (store.findAndUpdate(Collection.NODES, operations.get("/")) != null) { // clean up in-memory branch data // first revision is the ancestor (tailSet is inclusive) - List<Revision> revs = Lists.newArrayList(b.getCommits().tailSet(ancestor)); + List<Revision> revs = Lists.newArrayList(b.getCommits().tailSet(ancestor.getBranchRevision())); for (Revision r : revs.subList(1, revs.size())) { b.removeCommit(r); } @@ -1358,14 +1314,16 @@ public final class DocumentNodeStore } @Nonnull - Revision merge(@Nonnull Revision branchHead, @Nullable CommitInfo info) + RevisionVector merge(@Nonnull RevisionVector branchHead, + @Nullable CommitInfo info) throws CommitFailedException { Branch b = getBranches().getBranch(branchHead); - Revision base = branchHead; + RevisionVector base = branchHead; if (b != null) { - base = b.getBase(branchHead); + base = b.getBase(branchHead.getBranchRevision()); } int numBranchCommits = b != null ? b.getCommits().size() : 1; + RevisionVector newHead; boolean success = false; MergeCommit commit = newMergeCommit(base, numBranchCommits); try { @@ -1387,22 +1345,21 @@ public final class DocumentNodeStore getBranches().remove(b); } else { NodeDocument root = Utils.getRootDocument(store); - Revision conflictRev = root.getMostRecentConflictFor(b.getCommits(), this); + Set<Revision> conflictRevs = root.getConflictsFor(b.getCommits()); String msg = "Conflicting concurrent change. Update operation failed: " + op; - throw new ConflictException(msg, conflictRev).asCommitFailedException(); + throw new ConflictException(msg, conflictRevs).asCommitFailedException(); } } else { // no commits in this branch -> do nothing } + newHead = done(commit, false, info); success = true; } finally { if (!success) { canceled(commit); - } else { - done(commit, false, info); } } - return commit.getRevision(); + return newHead; } /** @@ -1494,7 +1451,7 @@ public final class DocumentNodeStore @Nonnull @Override public DocumentNodeState getRoot() { - return getRoot(headRevision); + return root; } @Nonnull @@ -1518,6 +1475,7 @@ public final class DocumentNodeStore } @Override + @Nonnull public BlobStoreBlob createBlob(InputStream inputStream) throws IOException { return new BlobStoreBlob(blobStore, blobStore.writeBlob(inputStream)); } @@ -1530,7 +1488,7 @@ public final class DocumentNodeStore * @return the blob. */ @Override - public Blob getBlob(String reference) { + public Blob getBlob(@Nonnull String reference) { String blobId = blobStore.getBlobId(reference); if(blobId != null){ return new BlobStoreBlob(blobStore, blobId); @@ -1578,19 +1536,13 @@ public final class DocumentNodeStore @CheckForNull @Override public NodeState retrieve(@Nonnull String checkpoint) { - Revision r; - try { - r = Revision.fromString(checkpoint); - } catch (IllegalArgumentException e) { - LOG.warn("Malformed checkpoint reference: {}", checkpoint); - return null; - } - SortedMap<Revision, Info> checkpoints = this.checkpoints.getCheckpoints(); - if (checkpoints != null && checkpoints.containsKey(r)) { - return getRoot(r); - } else { + RevisionVector rv = getCheckpoints().retrieve(checkpoint); + if (rv == null) { return null; } + // make sure all changes up to checkpoint are visible + suspendUntilAll(Sets.newHashSet(rv)); + return getRoot(rv); } @Override @@ -1612,18 +1564,13 @@ public final class DocumentNodeStore } @Override - public Revision.RevisionComparator getRevisionComparator() { - return revisionComparator; - } - - @Override public int getClusterId() { return clusterId; } @Nonnull - public Revision getHeadRevision() { - return headRevision; + public RevisionVector getHeadRevision() { + return root.getRevision(); } @Nonnull @@ -1729,49 +1676,53 @@ public final class DocumentNodeStore } /** - * Updates the state about cluster nodes in {@link #activeClusterNodes} - * and {@link #inactiveClusterNodes}. + * Updates the state about cluster nodes in {@link #clusterNodes}. + * * @return true if the cluster state has changed, false if the cluster state * remained unchanged */ boolean updateClusterState() { boolean hasChanged = false; - long now = clock.getTime(); - Set<Integer> inactive = Sets.newHashSet(); + Set<Integer> clusterIds = Sets.newHashSet(); for (ClusterNodeInfoDocument doc : ClusterNodeInfoDocument.all(store)) { int cId = doc.getClusterId(); - if (cId != this.clusterId && !doc.isActive()) { - inactive.add(cId); - } else { - hasChanged |= activeClusterNodes.put(cId, doc.getLeaseEndTime())==null; + clusterIds.add(cId); + ClusterNodeInfoDocument old = clusterNodes.get(cId); + // do not replace document for inactive cluster node + // in order to keep the created timestamp of the document + // for the time when the cluster node was first seen inactive + if (old != null && !old.isActive() && !doc.isActive()) { + continue; + } + clusterNodes.put(cId, doc); + if (old == null || old.isActive() != doc.isActive()) { + hasChanged = true; } } - hasChanged |= activeClusterNodes.keySet().removeAll(inactive); - hasChanged |= inactiveClusterNodes.keySet().retainAll(inactive); - for (Integer clusterId : inactive) { - hasChanged |= inactiveClusterNodes.putIfAbsent(clusterId, now)==null; - } + hasChanged |= clusterNodes.keySet().retainAll(clusterIds); return hasChanged; } /** - * Returns the cluster nodes currently known to be inactive. - * - * @return a map with the cluster id as key and the time in millis when it - * was first seen inactive. - */ - Map<Integer, Long> getInactiveClusterNodes() { - return new HashMap<Integer, Long>(inactiveClusterNodes); - } - - /** - * Returns the cluster nodes currently known as active. - * - * @return a map with the cluster id as key and the time in millis when the - * lease ends. + * @return the minimum revisions of foreign cluster nodes since they were + * started. The revision is derived from the start time of the + * cluster node. */ - Map<Integer, Long> getActiveClusterNodes() { - return new HashMap<Integer, Long>(activeClusterNodes); + @Nonnull + RevisionVector getMinExternalRevisions() { + return new RevisionVector(transform(filter(clusterNodes.values(), + new Predicate<ClusterNodeInfoDocument>() { + @Override + public boolean apply(ClusterNodeInfoDocument input) { + return input.getClusterId() != getClusterId(); + } + }), + new Function<ClusterNodeInfoDocument, Revision>() { + @Override + public Revision apply(ClusterNodeInfoDocument input) { + return new Revision(input.getStartTime(), 0, input.getClusterId()); + } + })); } /** @@ -1787,17 +1738,12 @@ public final class DocumentNodeStore } alignWithExternalRevisions(doc); - Revision.RevisionComparator revisionComparator = getRevisionComparator(); - // the (old) head occurred first - Revision headSeen = Revision.newRevision(0); - // then we saw this new revision (from another cluster node) - Revision otherSeen = Revision.newRevision(0); - StringSort externalSort = JournalEntry.newSorter(); Map<Integer, Revision> lastRevMap = doc.getLastRev(); try { - Map<Revision, Revision> externalChanges = Maps.newHashMap(); + RevisionVector headRevision = getHeadRevision(); + Set<Revision> externalChanges = Sets.newHashSet(); for (Map.Entry<Integer, Revision> e : lastRevMap.entrySet()) { int machineId = e.getKey(); if (machineId == clusterId) { @@ -1805,20 +1751,18 @@ public final class DocumentNodeStore continue; } Revision r = e.getValue(); - Revision last = lastKnownRevision.get(machineId); - if (last == null || r.compareRevisionTime(last) > 0) { - lastKnownRevision.put(machineId, r); + Revision last = headRevision.getRevision(machineId); + if (last == null) { + // make sure we see all changes when a cluster node joins + last = new Revision(0, 0, machineId); + } + if (r.compareRevisionTime(last) > 0) { // OAK-2345 // only consider as external change if - // - the revision changed for the machineId - // or - // - the revision is within the time frame we remember revisions - if (last != null - || r.getTimestamp() > revisionPurgeMillis()) { - externalChanges.put(r, otherSeen); - } + // the revision changed for the machineId + externalChanges.add(r); // collect external changes - if (last != null && externalSort != null) { + if (externalSort != null) { // add changes for this particular clusterId to the externalSort try { fillExternalChanges(externalSort, last, r, store); @@ -1871,30 +1815,24 @@ public final class DocumentNodeStore stats.cacheInvalidationTime = clock.getTime() - time; time = clock.getTime(); - // make sure update to revision comparator is atomic - // and no local commit is in progress + // make sure no local commit is in progress backgroundOperationLock.writeLock().lock(); try { stats.lock = clock.getTime() - time; - // the latest revisions of the current cluster node - // happened before the latest revisions of other cluster nodes - revisionComparator.add(newRevision(), headSeen); - // then we saw other revisions - for (Map.Entry<Revision, Revision> e : externalChanges.entrySet()) { - revisionComparator.add(e.getKey(), e.getValue()); + RevisionVector oldHead = getHeadRevision(); + RevisionVector newHead = oldHead; + for (Revision r : externalChanges) { + newHead = newHead.update(r); } - - Revision oldHead = headRevision; - // the new head revision is after other revisions - setHeadRevision(newRevision()); + setRoot(newHead); commitQueue.headRevisionChanged(); time = clock.getTime(); if (externalSort != null) { // then there were external changes and reading them // was successful -> apply them to the diff cache try { - JournalEntry.applyTo(externalSort, diffCache, oldHead, headRevision); + JournalEntry.applyTo(externalSort, diffCache, oldHead, newHead); } catch (Exception e1) { LOG.error("backgroundRead: Exception while processing external changes from journal: {}", e1, e1); } @@ -1907,13 +1845,10 @@ public final class DocumentNodeStore backgroundOperationLock.writeLock().unlock(); } stats.dispatchChanges = clock.getTime() - time; - time = clock.getTime(); } } finally { IOUtils.closeQuietly(externalSort); } - revisionComparator.purge(revisionPurgeMillis()); - stats.purge = clock.getTime() - time; return stats; } @@ -1925,7 +1860,6 @@ public final class DocumentNodeStore long populateDiffCache; long lock; long dispatchChanges; - long purge; @Override public String toString() { @@ -1940,21 +1874,10 @@ public final class DocumentNodeStore ", diff: " + populateDiffCache + ", lock:" + lock + ", dispatch:" + dispatchChanges + - ", purge:" + purge + '}'; } } - /** - * Returns the time in milliseconds when revisions can be purged from the - * revision comparator. - * - * @return time in milliseconds. - */ - private static long revisionPurgeMillis() { - return Revision.getCurrentTimestamp() - REMEMBER_REVISION_ORDER_MILLIS; - } - private void cleanOrphanedBranches() { Branch b; while ((b = branches.pollOrphanedBranch()) != null) { @@ -1975,7 +1898,7 @@ public final class DocumentNodeStore if (root == null) { return; } - Revision head = getHeadRevision(); + RevisionVector head = getHeadRevision(); Map<Revision, String> map = root.getLocalMap(NodeDocument.COLLISIONS); UpdateOp op = new UpdateOp(id, false); for (Revision r : map.keySet()) { @@ -1985,7 +1908,7 @@ public final class DocumentNodeStore // head. That is, the collision cannot be related to commit // which is progress. if (branches.getBranchCommit(r) == null - && isRevisionNewer(head, r)) { + && !head.isRevisionNewer(r)) { NodeDocument.removeCollision(op, r); } } @@ -1997,7 +1920,7 @@ public final class DocumentNodeStore } private void backgroundSplit() { - Revision head = getHeadRevision(); + RevisionVector head = getHeadRevision(); for (Iterator<String> it = splitCandidates.keySet().iterator(); it.hasNext();) { String id = it.next(); NodeDocument doc = store.find(Collection.NODES, id); @@ -2046,30 +1969,18 @@ public final class DocumentNodeStore //-----------------------------< internal >--------------------------------- /** - * Performs an initial read of the _lastRevs on the root document, - * initializes the {@link #revisionComparator} and sets the head revision. + * Performs an initial read of the _lastRevs on the root document and sets + * the root state. * * @param rootDoc the current root document. */ - private void initializeHeadRevision(NodeDocument rootDoc) { - checkState(headRevision == null); + private void initializeRootState(NodeDocument rootDoc) { + checkState(root == null); alignWithExternalRevisions(rootDoc); - Map<Integer, Revision> lastRevMap = rootDoc.getLastRev(); - Revision seenAt = Revision.newRevision(0); - long purgeMillis = revisionPurgeMillis(); - for (Map.Entry<Integer, Revision> entry : lastRevMap.entrySet()) { - Revision r = entry.getValue(); - if (r.getTimestamp() > purgeMillis) { - revisionComparator.add(r, seenAt); - } - if (entry.getKey() == clusterId) { - continue; - } - lastKnownRevision.put(entry.getKey(), entry.getValue()); - } - revisionComparator.purge(purgeMillis); - setHeadRevision(newRevision()); + RevisionVector headRevision = new RevisionVector( + rootDoc.getLastRev().values()).update(newRevision()); + setRoot(headRevision); } /** @@ -2108,7 +2019,7 @@ public final class DocumentNodeStore } @Nonnull - private Commit newTrunkCommit(@Nonnull Revision base) { + private Commit newTrunkCommit(@Nonnull RevisionVector base) { checkArgument(!checkNotNull(base).isBranch(), "base must not be a branch revision: " + base); @@ -2128,7 +2039,7 @@ public final class DocumentNodeStore } @Nonnull - private Commit newBranchCommit(@Nonnull Revision base, + private Commit newBranchCommit(@Nonnull RevisionVector base, @Nullable DocumentNodeStoreBranch branch) { checkArgument(checkNotNull(base).isBranch(), "base must be a branch revision: " + base); @@ -2200,7 +2111,8 @@ public final class DocumentNodeStore * and that list does not have the given child node. A <code>false</code> indicates that node <i>might</i> * exist */ - private boolean checkNodeNotExistsFromChildrenCache(String path, Revision rev) { + private boolean checkNodeNotExistsFromChildrenCache(String path, + RevisionVector rev) { if (PathUtils.denotesRoot(path)) { return false; } @@ -2247,8 +2159,8 @@ public final class DocumentNodeStore final long getChildrenDoneIn = debug ? now() : 0; String diffAlgo; - Revision fromRev = from.getLastRevision(); - Revision toRev = to.getLastRevision(); + RevisionVector fromRev = from.getLastRevision(); + RevisionVector toRev = to.getLastRevision(); if (!fromChildren.hasMore && !toChildren.hasMore) { diffAlgo = "diffFewChildren"; diffFewChildren(w, from.getPath(), fromChildren, @@ -2279,10 +2191,11 @@ public final class DocumentNodeStore return diff; } - private void diffManyChildren(JsopWriter w, String path, Revision fromRev, Revision toRev) { - long minTimestamp = Math.min( - revisionComparator.getMinimumTimestamp(fromRev, inactiveClusterNodes), - revisionComparator.getMinimumTimestamp(toRev, inactiveClusterNodes)); + private void diffManyChildren(JsopWriter w, String path, + RevisionVector fromRev, + RevisionVector toRev) { + long minTimestamp = Utils.getMinTimestampForDiff( + fromRev, toRev, getMinExternalRevisions()); long minValue = NodeDocument.getModifiedInSecs(minTimestamp); String fromKey = Utils.getKeyLowerLimit(path); String toKey = Utils.getKeyUpperLimit(path); @@ -2299,9 +2212,10 @@ public final class DocumentNodeStore // also consider nodes with not yet stored modifications (OAK-1107) Revision minRev = new Revision(minTimestamp, 0, getClusterId()); addPathsForDiff(path, paths, getPendingModifications().getPaths(minRev)); - for (Revision r : new Revision[]{fromRev, toRev}) { - if (r.isBranch()) { - Branch b = branches.getBranch(r); + for (RevisionVector rv : new RevisionVector[]{fromRev, toRev}) { + if (rv.isBranch()) { + Revision r = rv.getBranchRevision(); + Branch b = branches.getBranch(rv); if (b != null) { addPathsForDiff(path, paths, b.getModifiedPathsUntil(r)); } @@ -2319,8 +2233,8 @@ public final class DocumentNodeStore if (toNode != null) { // exists in both revisions // check if different - Revision a = fromNode.getLastRevision(); - Revision b = toNode.getLastRevision(); + RevisionVector a = fromNode.getLastRevision(); + RevisionVector b = toNode.getLastRevision(); if (a == null && b == null) { // ok } else if (a == null || b == null || !a.equals(b)) { @@ -2357,7 +2271,11 @@ public final class DocumentNodeStore } } - private void diffFewChildren(JsopWriter w, String parentPath, DocumentNodeState.Children fromChildren, Revision fromRev, DocumentNodeState.Children toChildren, Revision toRev) { + private void diffFewChildren(JsopWriter w, String parentPath, + DocumentNodeState.Children fromChildren, + RevisionVector fromRev, + DocumentNodeState.Children toChildren, + RevisionVector toRev) { Set<String> childrenSet = Sets.newHashSet(toChildren.children); for (String n : fromChildren.children) { if (!childrenSet.contains(n)) { @@ -2386,7 +2304,7 @@ public final class DocumentNodeStore } private static PathRev childNodeCacheKey(@Nonnull String path, - @Nonnull Revision readRevision, + @Nonnull RevisionVector readRevision, @Nullable String name) { String p = (name == null ? "" : name) + path; return new PathRev(p, readRevision); @@ -2417,7 +2335,8 @@ public final class DocumentNodeStore // of this commit i.e. transient nodes. If its required it would need to be looked // into - DocumentNodeState newNode = new DocumentNodeState(this, targetPath, commit.getRevision()); + RevisionVector destRevision = commit.getBaseRevision().update(commit.getRevision()); + DocumentNodeState newNode = new DocumentNodeState(this, targetPath, destRevision); source.copyTo(newNode); commit.addNode(newNode); @@ -2431,15 +2350,6 @@ public final class DocumentNodeStore } } - private void checkRevisionAge(Revision r, String path) { - if (LOG.isDebugEnabled()) { - if ("/".equals(path) && headRevision.getTimestamp() - r.getTimestamp() > WARN_REVISION_AGE) { - LOG.debug("Requesting an old revision for path " + path + ", " + - ((headRevision.getTimestamp() - r.getTimestamp()) / 1000) + " seconds old"); - } - } - } - /** * Creates and returns a MarkSweepGarbageCollector if the current BlobStore * supports garbage collection @@ -2502,12 +2412,12 @@ public final class DocumentNodeStore @Override public String getRevisionComparatorState() { - return revisionComparator.toString(); + return ""; } @Override public String getHead(){ - return headRevision.toString(); + return getRoot().getRevision().toString(); } @Override @@ -2522,33 +2432,50 @@ public final class DocumentNodeStore @Override public String[] getInactiveClusterNodes() { - return toArray(transform(inactiveClusterNodes.entrySet(), - new Function<Map.Entry<Integer, Long>, String>() { + return toArray(transform(filter(clusterNodes.values(), + new Predicate<ClusterNodeInfoDocument>() { + @Override + public boolean apply(ClusterNodeInfoDocument input) { + return !input.isActive(); + } + }), + new Function<ClusterNodeInfoDocument, String>() { @Override - public String apply(Map.Entry<Integer, Long> input) { - return input.toString(); + public String apply(ClusterNodeInfoDocument input) { + return input.getClusterId() + "=" + input.getCreated(); } }), String.class); } @Override public String[] getActiveClusterNodes() { - return toArray(transform(activeClusterNodes.entrySet(), - new Function<Map.Entry<Integer, Long>, String>() { + return toArray(transform(filter(clusterNodes.values(), + new Predicate<ClusterNodeInfoDocument>() { @Override - public String apply(Map.Entry<Integer, Long> input) { - return input.toString(); + public boolean apply(ClusterNodeInfoDocument input) { + return input.isActive(); + } + }), + new Function<ClusterNodeInfoDocument, String>() { + @Override + public String apply(ClusterNodeInfoDocument input) { + return input.getClusterId() + "=" + input.getLeaseEndTime(); } }), String.class); } @Override public String[] getLastKnownRevisions() { - return toArray(transform(lastKnownRevision.entrySet(), - new Function<Map.Entry<Integer, Revision>, String>() { + return toArray(transform(filter(getHeadRevision(), new Predicate<Revision>() { + @Override + public boolean apply(Revision input) { + return input.getClusterId() != getClusterId(); + } + }), + new Function<Revision, String>() { @Override - public String apply(Map.Entry<Integer, Revision> input) { - return input.toString(); + public String apply(Revision input) { + return input.getClusterId() + "=" + input.toString(); } }), String.class); }
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBranch.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBranch.java?rev=1723532&r1=1723531&r2=1723532&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBranch.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBranch.java Thu Jan 7 12:46:35 2016 @@ -34,6 +34,8 @@ import java.util.concurrent.locks.ReadWr import javax.annotation.CheckForNull; import javax.annotation.Nonnull; +import com.google.common.base.Function; +import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -279,7 +281,7 @@ class DocumentNodeStoreBranch implements CommitInfo info) { boolean success = false; Commit c = store.newCommit(base.getRevision(), this); - Revision rev; + RevisionVector rev; try { op.with(c); if (c.isEmpty()) { @@ -287,12 +289,11 @@ class DocumentNodeStoreBranch implements // finally clause cancel the commit return base; } - rev = c.apply(); + c.apply(); + rev = store.done(c, base.getRevision().isBranch(), info); success = true; } finally { - if (success) { - store.done(c, base.getRevision().isBranch(), info); - } else { + if (!success) { store.canceled(c); } } @@ -546,7 +547,7 @@ class DocumentNodeStoreBranch implements * @return the branch state. */ final DocumentNodeState createBranch(DocumentNodeState state) { - return store.getRoot(state.getRevision().asBranchRevision()); + return store.getRoot(state.getRevision().asBranchRevision(store.getClusterId())); } @Override @@ -641,8 +642,15 @@ class DocumentNodeStoreBranch implements return; } NodeDocument doc = Utils.getRootDocument(store.getDocumentStore()); - Set<Revision> collisions = doc.getLocalMap(COLLISIONS).keySet(); - Set<Revision> conflicts = Sets.intersection(collisions, b.getCommits()); + Set<Revision> collisions = Sets.newHashSet(doc.getLocalMap(COLLISIONS).keySet()); + Set<Revision> commits = Sets.newHashSet(Iterables.transform(b.getCommits(), + new Function<Revision, Revision>() { + @Override + public Revision apply(Revision input) { + return input.asTrunkRevision(); + } + })); + Set<Revision> conflicts = Sets.intersection(collisions, commits); if (!conflicts.isEmpty()) { throw new CommitFailedException(STATE, 2, "Conflicting concurrent change on branch commits " + conflicts); Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreMBean.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreMBean.java?rev=1723532&r1=1723531&r2=1723532&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreMBean.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreMBean.java Thu Jan 7 12:46:35 2016 @@ -25,6 +25,7 @@ import org.apache.jackrabbit.oak.commons public interface DocumentNodeStoreMBean { String TYPE = "DocumentNodeStore"; + @Deprecated String getRevisionComparatorState(); String getHead(); Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/JournalEntry.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/JournalEntry.java?rev=1723532&r1=1723531&r2=1723532&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/JournalEntry.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/JournalEntry.java Thu Jan 7 12:46:35 2016 @@ -93,8 +93,8 @@ public final class JournalEntry extends static void applyTo(@Nonnull StringSort externalSort, @Nonnull DiffCache diffCache, - @Nonnull Revision from, - @Nonnull Revision to) throws IOException { + @Nonnull RevisionVector from, + @Nonnull RevisionVector to) throws IOException { LOG.debug("applyTo: starting for {} to {}", from, to); // note that it is not de-duplicated yet LOG.debug("applyTo: sorting done."); Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevs.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevs.java?rev=1723532&r1=1723531&r2=1723532&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevs.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevs.java Thu Jan 7 12:46:35 2016 @@ -17,6 +17,7 @@ package org.apache.jackrabbit.oak.plugins.document; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; import javax.annotation.CheckForNull; @@ -28,17 +29,19 @@ import org.apache.jackrabbit.oak.plugins /** * Helper class to track when a node was last modified. */ -final class LastRevs { +final class LastRevs implements Iterable<Revision> { private final Map<Integer, Revision> revs; - private final Revision readRevision; + private final RevisionVector readRevision; private final Branch branch; private Revision branchRev; - LastRevs(Map<Integer, Revision> revs, Revision readRevision, Branch branch) { + LastRevs(Map<Integer, Revision> revs, + RevisionVector readRevision, + Branch branch) { this.revs = new HashMap<Integer, Revision>(revs); this.readRevision = readRevision; this.branch = branch; @@ -60,7 +63,7 @@ final class LastRevs { } rev = rev.asBranchRevision(); if (branch != null && branch.containsCommit(rev) - && readRevision.compareRevisionTime(rev) >= 0) { + && readRevision.getBranchRevision().compareRevisionTime(rev) >= 0) { branchRev = Utils.max(branchRev, rev); } } @@ -70,8 +73,8 @@ final class LastRevs { return branchRev; } - @Nonnull - Map<Integer, Revision> get() { - return revs; + @Override + public Iterator<Revision> iterator() { + return revs.values().iterator(); } } Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/LocalDiffCache.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/LocalDiffCache.java?rev=1723532&r1=1723531&r2=1723532&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/LocalDiffCache.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/LocalDiffCache.java Thu Jan 7 12:46:35 2016 @@ -54,8 +54,8 @@ public class LocalDiffCache extends Diff } @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) { RevisionsKey key = new RevisionsKey(from, to); @@ -72,8 +72,8 @@ public class LocalDiffCache extends Diff @Nonnull @Override - public Entry newEntry(final @Nonnull Revision from, - final @Nonnull Revision to, + public Entry newEntry(final @Nonnull RevisionVector from, + final @Nonnull RevisionVector to, boolean local /*ignored*/) { return new Entry() { private final Map<String, String> changesPerPath = Maps.newHashMap(); Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCache.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCache.java?rev=1723532&r1=1723531&r2=1723532&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCache.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCache.java Thu Jan 7 12:46:35 2016 @@ -56,8 +56,8 @@ public class MemoryDiffCache extends Dif @CheckForNull @Override - public String getChanges(@Nonnull final Revision from, - @Nonnull final Revision to, + public String getChanges(@Nonnull final RevisionVector from, + @Nonnull final RevisionVector to, @Nonnull final String path, @Nullable final Loader loader) { PathRev key = diffCacheKey(path, from, to); @@ -89,8 +89,8 @@ public class MemoryDiffCache extends Dif @Nonnull @Override - public Entry newEntry(@Nonnull Revision from, - @Nonnull Revision to, + public Entry newEntry(@Nonnull RevisionVector from, + @Nonnull RevisionVector to, boolean local /*ignored*/) { return new MemoryEntry(from, to); } @@ -103,10 +103,10 @@ public class MemoryDiffCache extends Dif protected class MemoryEntry implements Entry { - private final Revision from; - private final Revision to; + private final RevisionVector from; + private final RevisionVector to; - protected MemoryEntry(Revision from, Revision to) { + protected MemoryEntry(RevisionVector from, RevisionVector to) { this.from = checkNotNull(from); this.to = checkNotNull(to); } @@ -124,8 +124,8 @@ public class MemoryDiffCache extends Dif } private static PathRev diffCacheKey(@Nonnull String path, - @Nonnull Revision from, - @Nonnull Revision to) { + @Nonnull RevisionVector from, + @Nonnull RevisionVector to) { return new PathRev(from + path, to); } @@ -142,15 +142,15 @@ public class MemoryDiffCache extends Dif * @return {@code true} if there are cache entries that indicate the node * at the given path was modified between the two revisions. */ - private boolean isUnchanged(@Nonnull final Revision from, - @Nonnull final Revision to, + private boolean isUnchanged(@Nonnull final RevisionVector from, + @Nonnull final RevisionVector to, @Nonnull final String path) { return !denotesRoot(path) && isChildUnchanged(from, to, getParentPath(path), getName(path)); } - private boolean isChildUnchanged(@Nonnull final Revision from, - @Nonnull final Revision to, + private boolean isChildUnchanged(@Nonnull final RevisionVector from, + @Nonnull final RevisionVector to, @Nonnull final String parent, @Nonnull final String name) { PathRev parentKey = diffCacheKey(parent, from, to); Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/MergeCommit.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/MergeCommit.java?rev=1723532&r1=1723531&r2=1723532&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/MergeCommit.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/MergeCommit.java Thu Jan 7 12:46:35 2016 @@ -33,7 +33,7 @@ class MergeCommit extends Commit { private final Set<Revision> branchCommits = Sets.newHashSet(); MergeCommit(DocumentNodeStore nodeStore, - Revision baseRevision, + RevisionVector baseRevision, SortedSet<Revision> revisions) { super(nodeStore, revisions.last(), baseRevision, null); this.mergeRevs = revisions; @@ -52,7 +52,7 @@ class MergeCommit extends Commit { } @Override - public void applyToCache(Revision before, boolean isBranchCommit) { + public void applyToCache(RevisionVector before, boolean isBranchCommit) { // do nothing for a merge commit, only notify node // store about merged revisions nodeStore.revisionsMerged(branchCommits);
