Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java (revision 1798795) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java (working copy) @@ -1287,17 +1287,11 @@ //This is very unexpected situation - parent's child list declares the child to exist, while //its node state is null. Let's put some extra effort to do some logging String id = Utils.getIdFromPath(p); - String cachedDocStr, uncachedDocStr; - try { - cachedDocStr = store.find(Collection.NODES, id).asString(); - } catch (DocumentStoreException dse) { - cachedDocStr = dse.toString(); - } - try { - uncachedDocStr = store.find(Collection.NODES, id, 0).asString(); - } catch (DocumentStoreException dse) { - uncachedDocStr = dse.toString(); - } + String cachedDocStr = docAsString(id, true); + String uncachedDocStr = docAsString(id, false); + nodeCache.invalidate(new PathRev(p, readRevision)); + nodeChildrenCache.invalidate(childNodeCacheKey( + parent.getPath(), readRevision, name)); String exceptionMsg = String.format( "Aborting getChildNodes() - DocumentNodeState is null for %s at %s " + "{\"cachedDoc\":{%s}, \"uncachedDoc\":{%s}}", @@ -1307,6 +1301,24 @@ return result.withRootRevision(parent.getRootRevision(), parent.isFromExternalChange()); } + + private String docAsString(String id, boolean cached) { + try { + NodeDocument doc; + if (cached) { + doc = store.find(Collection.NODES, id); + } else { + doc = store.find(Collection.NODES, id, 0); + } + if (doc == null) { + return ""; + } else { + return doc.asString(); + } + } catch (DocumentStoreException e) { + return e.toString(); + } + } }); } Index: oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java =================================================================== --- oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java (revision 1798795) +++ oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java (working copy) @@ -16,6 +16,7 @@ */ package org.apache.jackrabbit.oak.plugins.document; +import static java.util.Collections.emptyList; 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; @@ -3058,6 +3059,43 @@ } } + // OAK-6351 + @Test + public void inconsistentNodeChildrenCache() throws Exception { + DocumentNodeStore ns = builderProvider.newBuilder().getNodeStore(); + NodeBuilder builder = ns.getRoot().builder(); + builder.child("a"); + builder.child("b"); + merge(ns, builder); + builder = ns.getRoot().builder(); + builder.child("b").remove(); + merge(ns, builder); + RevisionVector head = ns.getHeadRevision(); + + // simulate an incorrect cache entry + PathRev key = new PathRev("/", head); + DocumentNodeState.Children c = new DocumentNodeState.Children(); + c.children.add("a"); + c.children.add("b"); + ns.getNodeChildrenCache().put(key, c); + + try { + for (ChildNodeEntry entry : ns.getRoot().getChildNodeEntries()) { + entry.getName(); + } + fail("must fail with DocumentStoreException"); + } catch (DocumentStoreException e) { + // expected + } + // next attempt must succeed + List names = Lists.newArrayList(); + for (ChildNodeEntry entry : ns.getRoot().getChildNodeEntries()) { + names.add(entry.getName()); + } + assertEquals(1L, names.size()); + assertTrue(names.contains("a")); + } + private static class WriteCountingStore extends MemoryDocumentStore { private final ThreadLocal createMulti = new ThreadLocal<>(); int count;