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 ch...@apache.org on 2016/06/21 06:19:59 UTC

svn commit: r1749435 - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/plugins/document/ main/java/org/apache/jackrabbit/oak/plugins/document/secondary/ test/java/org/apache/jackrabbit/oak/plugins/document/secondary/

Author: chetanm
Date: Tue Jun 21 06:19:59 2016
New Revision: 1749435

URL: http://svn.apache.org/viewvc?rev=1749435&view=rev
Log:
OAK-4180 - Use another NodeStore as a local cache for a remote Document store

-- Changed cache logic to only provide a path for given lastRev. No need to work at parent lastRev
-- Changed DocumentNodeState to also check with cache for getChildNodeEntries  call

Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStateCache.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCache.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheTest.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java?rev=1749435&r1=1749434&r2=1749435&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java Tue Jun 21 06:19:59 2016
@@ -25,6 +25,7 @@ import java.util.Map.Entry;
 import java.util.NoSuchElementException;
 import java.util.Set;
 
+import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
@@ -85,6 +86,8 @@ public class DocumentNodeState extends A
 
     private final DocumentNodeStore store;
 
+    private AbstractDocumentNodeState cachedSecondaryState;
+
     DocumentNodeState(@Nonnull DocumentNodeStore store,
                       @Nonnull String path,
                       @Nonnull RevisionVector readRevision) {
@@ -222,8 +225,7 @@ public class DocumentNodeState extends A
         if (!hasChildren || !isValidName(name)) {
             return false;
         } else {
-            String p = PathUtils.concat(getPath(), name);
-            return store.getNode(p, rootRevision, lastRevision) != null;
+            return getChildNodeDoc(name) != null;
         }
     }
 
@@ -234,8 +236,7 @@ public class DocumentNodeState extends A
             checkValidName(name);
             return EmptyNodeState.MISSING_NODE;
         }
-        String p = PathUtils.concat(getPath(), name);
-        AbstractDocumentNodeState child = store.getNode(p, rootRevision, lastRevision);
+        AbstractDocumentNodeState child = getChildNodeDoc(name);
         if (child == null) {
             checkValidName(name);
             return EmptyNodeState.MISSING_NODE;
@@ -273,6 +274,12 @@ public class DocumentNodeState extends A
         if (!hasChildren) {
             return Collections.emptyList();
         }
+
+        AbstractDocumentNodeState secondaryState = getSecondaryNodeState();
+        if (secondaryState != null){
+            return secondaryState.getChildNodeEntries();
+        }
+
         return new Iterable<ChildNodeEntry>() {
             @Override
             public Iterator<ChildNodeEntry> iterator() {
@@ -433,6 +440,29 @@ public class DocumentNodeState extends A
 
     //------------------------------< internal >--------------------------------
 
+    @CheckForNull
+    private AbstractDocumentNodeState getChildNodeDoc(String childNodeName){
+        AbstractDocumentNodeState secondaryState = getSecondaryNodeState();
+        if (secondaryState != null){
+            NodeState result = secondaryState.getChildNode(childNodeName);
+            //If given child node exist then cast it and return
+            //else return null
+            if (result.exists()){
+                return (AbstractDocumentNodeState) result;
+            }
+            return null;
+        }
+        return store.getNode(PathUtils.concat(getPath(), childNodeName), lastRevision);
+    }
+
+    @CheckForNull
+    private AbstractDocumentNodeState getSecondaryNodeState(){
+        if (cachedSecondaryState == null){
+            cachedSecondaryState = store.getSecondaryNodeState(getPath(), rootRevision, lastRevision);
+        }
+        return cachedSecondaryState;
+    }
+
     /**
      * Returns {@code true} if this state has the same revision as the
      * {@code other} state. This method first compares the {@link #readRevision}

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStateCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStateCache.java?rev=1749435&r1=1749434&r2=1749435&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStateCache.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStateCache.java Tue Jun 21 06:19:59 2016
@@ -19,68 +19,31 @@
 
 package org.apache.jackrabbit.oak.plugins.document;
 
-import javax.annotation.Nonnull;
+import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 
-import static com.google.common.base.Preconditions.checkState;
 
 public interface DocumentNodeStateCache {
     DocumentNodeStateCache NOOP = new DocumentNodeStateCache() {
-        @Nonnull
         @Override
-        public NodeStateCacheEntry getDocumentNodeState(String path, @Nullable RevisionVector rootRevision,
-                                                        RevisionVector parentLastRev) {
-            return UNKNOWN;
+        public AbstractDocumentNodeState getDocumentNodeState(String path, @Nullable RevisionVector rootRevision,
+                                                        RevisionVector lastRev) {
+            return null;
         }
     };
 
-    NodeStateCacheEntry MISSING = new NodeStateCacheEntry(NodeStateCacheEntry.EntryType.MISSING);
-
-    NodeStateCacheEntry UNKNOWN = new NodeStateCacheEntry(NodeStateCacheEntry.EntryType.UNKNOWN);
-
     /**
      * Get the node for the given path and revision.
      *
      * @param path the path of the node.
-     * @param rootRevision
-     * @param readRevision the read revision.
-     * @return the node or {@link MISSING} if no state is there for given path and revision or {@link UNKNOWN} if
-     * cache does not have any knowledge of nodeState for given parameters
+     * @param rootRevision revision of root NodeState
+     * @param lastRev last revision of the node at given path
+     *
+     * @return nodeState at given path or null. If given revision is not present or the
+     * path is not cached then <code>null</code> would be returned
      */
-    @Nonnull
-    NodeStateCacheEntry getDocumentNodeState(String path, RevisionVector rootRevision,
-                                             RevisionVector parentLastRev);
-
-    class NodeStateCacheEntry {
-        private enum EntryType {FOUND, MISSING, UNKNOWN}
-        private final AbstractDocumentNodeState state;
-        private final EntryType entryType;
-
-        public NodeStateCacheEntry(AbstractDocumentNodeState state) {
-            this.state = state;
-            this.entryType = EntryType.FOUND;
-        }
-
-        private NodeStateCacheEntry(EntryType entryType) {
-            this.state = null;
-            this.entryType = entryType;
-        }
+    @CheckForNull
+    AbstractDocumentNodeState getDocumentNodeState(String path, RevisionVector rootRevision, RevisionVector lastRev);
 
-        public AbstractDocumentNodeState getState(){
-            checkState(entryType == EntryType.FOUND, "Cannot read state from an entry of type [%s]", entryType);
-            return state;
-        }
-
-        public boolean isUnknown(){
-            return entryType == EntryType.UNKNOWN;
-        }
 
-        public boolean isMissing(){
-            return entryType == EntryType.MISSING;
-        }
-
-        public boolean isFound(){
-            return entryType == EntryType.FOUND;
-        }
-    }
 }

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=1749435&r1=1749434&r2=1749435&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 Tue Jun 21 06:19:59 2016
@@ -872,18 +872,11 @@ public final class DocumentNodeStore
     }
 
     @CheckForNull
-    AbstractDocumentNodeState getNode(@Nonnull final String path,
+    AbstractDocumentNodeState getSecondaryNodeState(@Nonnull final String path,
                               @Nonnull final RevisionVector rootRevision,
                               @Nonnull final RevisionVector rev) {
         //Check secondary cache first
-        NodeStateCacheEntry entry  = nodeStateCache.getDocumentNodeState(path, rootRevision, rev);
-        if (entry.isMissing()){
-            return null;
-        } else if (entry.isFound()){
-            return entry.getState();
-        }
-
-        return getNode(path, rev);
+        return nodeStateCache.getDocumentNodeState(path, rootRevision, rev);
     }
 
     /**

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCache.java?rev=1749435&r1=1749434&r2=1749435&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCache.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCache.java Tue Jun 21 06:19:59 2016
@@ -76,29 +76,28 @@ class SecondaryStoreCache implements Doc
         this.queue = EvictingQueue.create(maxSize);
     }
 
-    @Nonnull
+    @CheckForNull
     @Override
-    public NodeStateCacheEntry getDocumentNodeState(String path, RevisionVector rootRevision,
-                                                    RevisionVector parentLastRev) {
+    public AbstractDocumentNodeState getDocumentNodeState(String path, RevisionVector rootRevision,
+                                                    RevisionVector lastRev) {
         //TODO We might need skip the calls if they occur due to SecondaryStoreObserver
         //doing the diff or in the startup when we try to sync the state
         PathFilter.Result result = pathFilter.filter(path);
         if (result != PathFilter.Result.INCLUDE) {
             unknownPaths.mark();
-            return DocumentNodeStateCache.UNKNOWN;
+            return null;
         }
 
         if (!DelegatingDocumentNodeState.hasMetaProps(store.getRoot())){
-            return DocumentNodeStateCache.UNKNOWN;
+            return null;
         }
 
         AbstractDocumentNodeState currentRoot = DelegatingDocumentNodeState.wrap(store.getRoot(), differ);
 
-        NodeStateCacheEntry cacheEntryResult = findByMatchingParentLastRev(currentRoot, path,
-                rootRevision, parentLastRev);
-        if (cacheEntryResult != DocumentNodeStateCache.UNKNOWN){
+        AbstractDocumentNodeState nodeState = findByMatchingLastRev(currentRoot, path, lastRev);
+        if (nodeState != null){
             headRevMatched.mark();
-            return cacheEntryResult;
+            return nodeState;
         }
 
         AbstractDocumentNodeState matchingRoot = findMatchingRoot(rootRevision);
@@ -107,45 +106,32 @@ class SecondaryStoreCache implements Doc
             if (state.exists()){
                 AbstractDocumentNodeState docState = (AbstractDocumentNodeState) state;
                 prevRevMatched.mark();
-                return new NodeStateCacheEntry(docState);
-            } else {
-                return DocumentNodeStateCache.MISSING;
+                return docState;
             }
         }
 
-        //TODO Check in tail if rootRevision is not in our maintained list of root
         knownMissed.mark();
-        return DocumentNodeStateCache.UNKNOWN;
+        return null;
     }
 
-    @Nonnull
-    private NodeStateCacheEntry findByMatchingParentLastRev(AbstractDocumentNodeState root, String path,
-                                                            RevisionVector rootRevision, RevisionVector parentLastRev){
-        NodeState parentNodeState = root;
+    @CheckForNull
+    private AbstractDocumentNodeState findByMatchingLastRev(AbstractDocumentNodeState root, String path,
+                                                      RevisionVector lastRev){
         NodeState state = root;
 
-        //Get the parent node state
         for (String name : PathUtils.elements(path)) {
-            parentNodeState = state;
             state = state.getChildNode(name);
         }
 
-        if (parentNodeState.exists()) {
-            AbstractDocumentNodeState parentDocState = (AbstractDocumentNodeState) parentNodeState;
-            //So parent state exists and matches the expected revision
-            if (parentLastRev.equals(parentDocState.getLastRevision())) {
+        if (state.exists()) {
+            AbstractDocumentNodeState docState = (AbstractDocumentNodeState) state;
+            if (lastRev.equals(docState.getLastRevision())) {
                 headRevMatched.mark();
-                if (state.exists()) {
-                    AbstractDocumentNodeState stateAtExpectedRootRev =
-                            ((AbstractDocumentNodeState) state).withRootRevision(rootRevision, false);
-                    return new NodeStateCacheEntry(stateAtExpectedRootRev);
-                } else {
-                    return DocumentNodeStateCache.MISSING;
-                }
+                return docState;
             }
         }
 
-        return DocumentNodeStateCache.UNKNOWN;
+        return null;
     }
 
     @CheckForNull

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheTest.java?rev=1749435&r1=1749434&r2=1749435&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/secondary/SecondaryStoreCacheTest.java Tue Jun 21 06:19:59 2016
@@ -25,10 +25,9 @@ import java.util.List;
 
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.plugins.document.AbstractDocumentNodeState;
 import org.apache.jackrabbit.oak.plugins.document.DocumentMKBuilderProvider;
-import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStateCache;
-import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStateCache.NodeStateCacheEntry;
 import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore;
 import org.apache.jackrabbit.oak.plugins.document.Revision;
 import org.apache.jackrabbit.oak.plugins.document.RevisionVector;
@@ -48,7 +47,10 @@ import static com.google.common.collect.
 import static org.apache.jackrabbit.oak.plugins.document.NodeStateDiffer.DEFAULT_DIFFER;
 import static org.apache.jackrabbit.oak.plugins.document.secondary.SecondaryStoreObserverTest.create;
 import static org.apache.jackrabbit.oak.plugins.document.secondary.SecondaryStoreObserverTest.documentState;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 public class SecondaryStoreCacheTest {
     private final List<String> empty = Collections.emptyList();
@@ -66,104 +68,82 @@ public class SecondaryStoreCacheTest {
 
     @Test
     public void basicTest() throws Exception{
-        PathFilter pathFilter = new PathFilter(of("/a"), empty);
-        SecondaryStoreObserver observer = new SecondaryStoreObserver(secondary, pathFilter, DEFAULT_DIFFER);
-        primary.addObserver(observer);
-
-        SecondaryStoreCache cache = new SecondaryStoreCache(secondary, pathFilter, DEFAULT_DIFFER);
+        SecondaryStoreCache cache = createCache(new PathFilter(of("/a"), empty));
 
         NodeBuilder nb = primary.getRoot().builder();
         create(nb, "/a/b", "/a/c", "/x/y/z");
-        primary.merge(nb, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        merge(nb);
 
         RevisionVector rv1 = new RevisionVector(new Revision(1,0,1));
         RevisionVector rv2 = new RevisionVector(new Revision(1,0,3));
-        assertSame(DocumentNodeStateCache.UNKNOWN, cache.getDocumentNodeState("/a/b", rv1, rv2));
-        assertSame(DocumentNodeStateCache.UNKNOWN, cache.getDocumentNodeState("/x", rv1, rv2));
+        assertNull(cache.getDocumentNodeState("/a/b", rv1, rv2));
+        assertNull(cache.getDocumentNodeState("/x", rv1, rv2));
     }
 
     @Test
     public void updateAndReadAtReadRev() throws Exception{
-        PathFilter pathFilter = new PathFilter(of("/a"), empty);
-        SecondaryStoreObserver observer = new SecondaryStoreObserver(secondary, pathFilter, DEFAULT_DIFFER);
-        primary.addObserver(observer);
-
-        SecondaryStoreCache cache = new SecondaryStoreCache(secondary, pathFilter, DEFAULT_DIFFER);
+        SecondaryStoreCache cache = createCache(new PathFilter(of("/a"), empty));
 
         NodeBuilder nb = primary.getRoot().builder();
         create(nb, "/a/b", "/a/c", "/x/y/z");
-        AbstractDocumentNodeState r1 =
-                (AbstractDocumentNodeState) primary.merge(nb, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        AbstractDocumentNodeState r1 = merge(nb);
 
         //Update some other part of tree i.e. which does not change lastRev for /a/c
         nb = primary.getRoot().builder();
         create(nb, "/a/e/d");
-        AbstractDocumentNodeState r2 =
-                (AbstractDocumentNodeState)primary.merge(nb, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        AbstractDocumentNodeState r2 = merge(nb);
 
         //Lookup should work fine
-        AbstractDocumentNodeState a_r2 = documentState(r2, "/a");
-        NodeStateCacheEntry result
+        AbstractDocumentNodeState a_r2 = documentState(r2, "/a/c");
+        AbstractDocumentNodeState result
                 = cache.getDocumentNodeState("/a/c", r2.getRootRevision(), a_r2.getLastRevision());
-        assertTrue(EqualsDiff.equals(a_r2.getChildNode("c"), result.getState()));
+        assertTrue(EqualsDiff.equals(a_r2, result));
 
         nb = primary.getRoot().builder();
         nb.child("a").child("c").remove();
-        primary.merge(nb, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        AbstractDocumentNodeState r3 = merge(nb);
 
         //Now look from older revision
-        result = cache.getDocumentNodeState("/a/c", r1.getRootRevision(), a_r2.getLastRevision());
+        result = cache.getDocumentNodeState("/a/c", r3.getRootRevision(), a_r2.getLastRevision());
 
         //now as its not visible from head it would not be visible
-        assertSame(DocumentNodeStateCache.UNKNOWN, result);
+        assertNull(result);
     }
 
     @Test
     public void updateAndReadAtPrevRevision() throws Exception {
-        PathFilter pathFilter = new PathFilter(of("/a"), empty);
-        SecondaryStoreCache cache = new SecondaryStoreCache(secondary, pathFilter, DEFAULT_DIFFER);
-        SecondaryStoreObserver observer = new SecondaryStoreObserver(secondary, pathFilter, cache,
-                DEFAULT_DIFFER, StatisticsProvider.NOOP);
-        primary.addObserver(observer);
+        SecondaryStoreCache cache = createCache(new PathFilter(of("/a"), empty));
 
         NodeBuilder nb = primary.getRoot().builder();
         create(nb, "/a/b", "/a/c");
-        AbstractDocumentNodeState r0 =
-                (AbstractDocumentNodeState) primary.merge(nb, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        AbstractDocumentNodeState r0 = merge(nb);
         AbstractDocumentNodeState a_c_0 = documentState(primary.getRoot(), "/a/c");
 
         //Update some other part of tree i.e. which does not change lastRev for /a/c
         nb = primary.getRoot().builder();
         create(nb, "/a/c/d");
-        AbstractDocumentNodeState r1 =
-                (AbstractDocumentNodeState)primary.merge(nb, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        AbstractDocumentNodeState r1 = merge(nb);
         AbstractDocumentNodeState a_c_1 = documentState(primary.getRoot(), "/a/c");
 
-        NodeStateCacheEntry result
+        AbstractDocumentNodeState result
                 = cache.getDocumentNodeState("/a/c", r1.getRootRevision(), a_c_1.getLastRevision());
-        assertTrue(EqualsDiff.equals(a_c_1, result.getState()));
+        assertTrue(EqualsDiff.equals(a_c_1, result));
 
         //Read from older revision
-        result
-                = cache.getDocumentNodeState("/a/c", r0.getRootRevision(), a_c_0.getLastRevision());
-        assertTrue(EqualsDiff.equals(a_c_0, result.getState()));
+        result = cache.getDocumentNodeState("/a/c", r0.getRootRevision(), a_c_0.getLastRevision());
+        assertTrue(EqualsDiff.equals(a_c_0, result));
     }
 
     @Test
     public void binarySearch() throws Exception{
-        PathFilter pathFilter = new PathFilter(of("/a"), empty);
-        SecondaryStoreCache cache = new SecondaryStoreCache(secondary, pathFilter, DEFAULT_DIFFER);
-        SecondaryStoreObserver observer = new SecondaryStoreObserver(secondary, pathFilter, cache,
-                DEFAULT_DIFFER, StatisticsProvider.NOOP);
-        primary.addObserver(observer);
+        SecondaryStoreCache cache = createCache(new PathFilter(of("/a"), empty));
 
         List<AbstractDocumentNodeState> roots = Lists.newArrayList();
         List<RevisionVector> revs = Lists.newArrayList();
         for (int i = 0; i < 50; i++) {
             NodeBuilder nb = primary.getRoot().builder();
             create(nb, "/a/b"+i);
-            AbstractDocumentNodeState r =
-                    (AbstractDocumentNodeState) primary.merge(nb, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+            AbstractDocumentNodeState r = merge(nb);
             roots.add(r);
             revs.add(r.getRootRevision());
         }
@@ -179,11 +159,23 @@ public class SecondaryStoreCacheTest {
 
         NodeBuilder nb = primary.getRoot().builder();
         create(nb, "/a/m");
-        AbstractDocumentNodeState r =
-                (AbstractDocumentNodeState) primary.merge(nb, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        AbstractDocumentNodeState r = merge(nb);
         AbstractDocumentNodeState result = SecondaryStoreCache.findMatchingRoot(rootsArr, r.getRootRevision());
         assertNull(result);
 
     }
 
+    private SecondaryStoreCache createCache(PathFilter pathFilter){
+        SecondaryStoreCache cache = new SecondaryStoreCache(secondary, pathFilter, DEFAULT_DIFFER);
+        SecondaryStoreObserver observer = new SecondaryStoreObserver(secondary, pathFilter, cache,
+                DEFAULT_DIFFER, StatisticsProvider.NOOP);
+        primary.addObserver(observer);
+
+        return cache;
+    }
+
+    private AbstractDocumentNodeState merge(NodeBuilder nb) throws CommitFailedException {
+        return (AbstractDocumentNodeState) primary.merge(nb, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+    }
+
 }
\ No newline at end of file