NodeDocument extends Document { @Nullable Revision getNewestRevision(final RevisionContext context, final RevisionVector baseRev, final Revision changeRev, final Branch branch, final Set<Revision> collisions) { checkArgument(!baseRev.isBranch() || branch != null, "Branch must be non-null if baseRev is a branch revision"); RevisionVector head = context.getHeadRevision(); RevisionVector lower = branch != null ? branch.getBase() : baseRev; Set<Integer> clusterIds = Collections.emptySet(); if (!getPreviousRanges().isEmpty()) { clusterIds = Sets.newHashSet(); for (Revision prevRev : getPreviousRanges().keySet()) { if (lower.isRevisionNewer(prevRev) || equal(prevRev, lower.getRevision(prevRev.getClusterId()))) { clusterIds.add(prevRev.getClusterId()); } } if (!clusterIds.isEmpty()) { for (Revision r : getLocalCommitRoot().keySet()) { clusterIds.add(r.getClusterId()); } for (Revision r : getLocalRevisions().keySet()) { clusterIds.add(r.getClusterId()); } } } boolean fullScan = true; Iterable<Revision> changes = Iterables.mergeSorted( ImmutableList.of( getLocalRevisions().keySet(), getLocalCommitRoot().keySet()), getLocalRevisions().comparator() ); if (!clusterIds.isEmpty()) { fullScan = false; changes = Iterables.mergeSorted( ImmutableList.of( changes, getChanges(REVISIONS, lower), getChanges(COMMIT_ROOT, lower) ), getLocalRevisions().comparator() ); if (LOG.isDebugEnabled()) { LOG.debug("getNewestRevision() with changeRev {} on {}, " + "_revisions {}, _commitRoot {}", changeRev, getId(), getLocalRevisions(), getLocalCommitRoot()); } } Map<Integer, Revision> newestRevs = Maps.newHashMap(); Map<Revision, String> validRevisions = Maps.newHashMap(); for (Revision r : changes) { if (r.equals(changeRev)) { continue; } if (!fullScan) { if (clusterIds.contains(r.getClusterId()) && !lower.isRevisionNewer(r) && newestRevs.containsKey(r.getClusterId())) { clusterIds.remove(r.getClusterId()); if (clusterIds.isEmpty()) { break; } } } if (newestRevs.containsKey(r.getClusterId())) { if (branch != null && !branch.containsCommit(r)) { if (branch.getBase(changeRev).isRevisionNewer(r)) { collisions.add(r); } } } else { if (isValidRevision(context, r, null, baseRev, validRevisions)) { newestRevs.put(r.getClusterId(), r); } else { Revision commitRevision = null; String cv = context.getCommitValue(r, this); if (Utils.isCommitted(cv)) { commitRevision = resolveCommitRevision(r, cv); } if (commitRevision != null && head.isRevisionNewer(commitRevision)) { collisions.add(r); } else if (commitRevision != null && branch == null && baseRev.isRevisionNewer(r)) { newestRevs.put(r.getClusterId(), r); } else { collisions.add(r); } } } } Revision newestRev = null; for (Revision r : newestRevs.values()) { newestRev = Utils.max(newestRev, r, StableRevisionComparator.INSTANCE); } if (newestRev == null) { return null; } SortedMap<Revision, String> deleted = getLocalDeleted(); String value = deleted.get(newestRev); if (value == null && deleted.headMap(newestRev).isEmpty()) { return newestRev; } if (value == null) { value = getDeleted().get(newestRev); } if ("true".equals(value)) { return null; } return newestRev; } NodeDocument(@NotNull DocumentStore store);  NodeDocument(@NotNull DocumentStore store, long creationTime); static long getModifiedInSecs(long timestamp); @NotNull Map<Revision, String> getValueMap(@NotNull String key); long getCreated(); @Nullable Long getModified(); boolean hasChildren(); boolean wasDeletedOnce(); boolean hasBeenModifiedSince(long lastModifiedTime); boolean hasAllRevisionLessThan(long maxRevisionTime); boolean isSplitDocument(); SplitDocType getSplitDocType(); void markUpToDate(long checkTime); long getLastCheckTime(); boolean hasBinary(); @NotNull Path getMainPath(); @NotNull Map<Integer, Revision> getLastRev(); boolean containsRevision(@NotNull Revision revision); @Nullable Path getCommitRootPath(Revision revision); @Nullable DocumentNodeState getNodeAtRevision(@NotNull DocumentNodeStore nodeStore,
                                               @NotNull RevisionVector readRevision,
                                               @Nullable Revision lastModified); @Nullable Revision getLiveRevision(RevisionContext context,
                                    RevisionVector readRevision,
                                    Map<Revision, String> validRevisions,
                                    LastRevs lastRevs); @NotNull Iterable<UpdateOp> split(@NotNull RevisionContext context,
                                    @NotNull RevisionVector head,
                                    @NotNull Function<String, Long> binarySize); @NotNull Set<Revision> getLocalBranchCommits(); static void setChildrenFlag(@NotNull UpdateOp op,
                                       boolean hasChildNode); static void setModified(@NotNull UpdateOp op,
                                   @NotNull Revision revision); static void setRevision(@NotNull UpdateOp op,
                                   @NotNull Revision revision,
                                   @NotNull String commitValue); static void unsetRevision(@NotNull UpdateOp op,
                                     @NotNull Revision revision); static boolean isRevisionsEntry(String name); static boolean isCommitRootEntry(String name); static boolean isDeletedEntry(String name); static boolean isLastRevEntry(String name); static void removeRevision(@NotNull UpdateOp op,
                                      @NotNull Revision revision); static void addCollision(@NotNull UpdateOp op,
                                    @NotNull Revision revision,
                                    @NotNull Revision other); static void removeCollision(@NotNull UpdateOp op,
                                       @NotNull Revision revision); static void setLastRev(@NotNull UpdateOp op,
                                  @NotNull Revision revision); static void setCommitRoot(@NotNull UpdateOp op,
                                     @NotNull Revision revision,
                                     int commitRootDepth); static void removeCommitRoot(@NotNull UpdateOp op,
                                        @NotNull Revision revision); static void unsetCommitRoot(@NotNull UpdateOp op,
                                       @NotNull Revision revision); static void setDeleted(@NotNull UpdateOp op,
                                  @NotNull Revision revision,
                                  boolean deleted); static void setDeletedOnce(@NotNull UpdateOp op); static void removeDeleted(@NotNull UpdateOp op,
                                     @NotNull Revision revision); static void setPrevious(@NotNull UpdateOp op,
                                   @NotNull Range range); static void removePrevious(@NotNull UpdateOp op,
                                      @NotNull Range range); static void removePrevious(@NotNull UpdateOp op,
                                      @NotNull Revision revision); static void setStalePrevious(@NotNull UpdateOp op,
                                        @NotNull Revision revision,
                                        int height); static void removeStalePrevious(@NotNull UpdateOp op,
                                           @NotNull Revision revision); static void setHasBinary(@NotNull UpdateOp op); static void setBranchCommit(@NotNull UpdateOp op,
                                       @NotNull Revision revision); static void removeBranchCommit(@NotNull UpdateOp op,
                                          @NotNull Revision revision); static void setSweepRevision(@NotNull UpdateOp op,
                                        @NotNull Revision revision); static void hasLastRev(@NotNull UpdateOp op,
                                  @NotNull Revision revision); @NotNull Path getPath(); String asString(); static NodeDocument fromString(DocumentStore store, String s); static final NodeDocument NULL; static final String MIN_ID_VALUE; static final String MAX_ID_VALUE; static final String COLLISIONS; static final String MODIFIED_IN_SECS; static final String DELETED_ONCE; static final String PATH; static final String HAS_BINARY_FLAG; static final String SD_TYPE; static final String SD_MAX_REV_TIME_IN_SECS; static final long HAS_BINARY_VAL; }
@Test public void getNewestRevision() throws Exception { MemoryDocumentStore store = new MemoryDocumentStore(); DocumentNodeStore ns1 = createTestStore(store, 1, 0); DocumentNodeStore ns2 = createTestStore(store, 2, 0); NodeBuilder b1 = ns1.getRoot().builder(); b1.child("test"); merge(ns1, b1); RevisionVector headCreated = ns1.getHeadRevision(); Revision created = headCreated.getRevision(ns1.getClusterId()); NodeDocument doc = store.find(NODES, Utils.getIdFromPath("/test")); Set<Revision> collisions = newHashSet(); Revision newest = doc.getNewestRevision(ns1, ns1.getHeadRevision(), ns1.newRevision(), null, collisions); assertEquals(created, newest); assertEquals(0, collisions.size()); newest = doc.getNewestRevision(ns2, ns2.getHeadRevision(), ns2.newRevision(), null, collisions); assertNull(newest); assertEquals(1, collisions.size()); assertEquals(created, collisions.iterator().next()); ns1.runBackgroundOperations(); ns2.runBackgroundOperations(); collisions.clear(); doc = store.find(NODES, Utils.getIdFromPath("/test")); newest = doc.getNewestRevision(ns2, ns2.getHeadRevision(), ns2.newRevision(), null, collisions); assertEquals(created, newest); assertEquals(0, collisions.size()); Revision uncommitted = ns1.newRevision(); UpdateOp op = new UpdateOp(Utils.getIdFromPath("/test"), false); NodeDocument.setCommitRoot(op, uncommitted, 0); op.setMapEntry("p", uncommitted, "v"); assertNotNull(store.findAndUpdate(NODES, op)); collisions.clear(); doc = store.find(NODES, Utils.getIdFromPath("/test")); newest = doc.getNewestRevision(ns1, ns1.getHeadRevision(), ns1.newRevision(), null, collisions); assertEquals(created, newest); assertEquals(1, collisions.size()); assertEquals(uncommitted, collisions.iterator().next()); collisions.clear(); newest = doc.getNewestRevision(ns2, ns2.getHeadRevision(), ns2.newRevision(), null, collisions); assertEquals(created, newest); assertEquals(1, collisions.size()); assertEquals(uncommitted, collisions.iterator().next()); b1 = ns1.getRoot().builder(); b1.child("test").setProperty("q", "v"); merge(ns1, b1); Revision committed = ns1.getHeadRevision().getRevision(ns1.getClusterId()); collisions.clear(); doc = store.find(NODES, Utils.getIdFromPath("/test")); newest = doc.getNewestRevision(ns1, headCreated, ns1.newRevision(), null, collisions); assertEquals(committed, newest); assertEquals(0, collisions.size()); newest = doc.getNewestRevision(ns2, ns2.getHeadRevision(), ns2.newRevision(), null, collisions); assertEquals(created, newest); assertEquals(2, collisions.size()); assertTrue(collisions.contains(uncommitted)); assertTrue(collisions.contains(committed)); ns1.dispose(); ns2.dispose(); }
@Test public void getNewestRevisionCheckArgument() throws Exception { MemoryDocumentStore store = new MemoryDocumentStore(); DocumentNodeStore ns = createTestStore(store, 0, 0); NodeBuilder builder = ns.getRoot().builder(); builder.child("test"); merge(ns, builder); Set<Revision> collisions = newHashSet(); NodeDocument doc = store.find(NODES, Utils.getIdFromPath("/test")); RevisionVector branchBase = ns.getHeadRevision().asBranchRevision(ns.getClusterId()); try { doc.getNewestRevision(ns, branchBase, ns.newRevision(), null, collisions); fail("Must fail with IllegalArgumentException"); } catch (IllegalArgumentException e) { } try { 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"); } catch (IllegalArgumentException e) { } ns.dispose(); }