Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/cache/ModificationStamp.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/cache/ModificationStamp.java (nonexistent) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/cache/ModificationStamp.java (working copy) @@ -0,0 +1,29 @@ +/* + * 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.cache; + +public final class ModificationStamp { + + public final long modCount; + + public final long modified; + + public ModificationStamp(long modCount, long modified) { + this.modCount = modCount; + this.modified = modified; + } +} Property changes on: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/cache/ModificationStamp.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/cache/NodeDocumentCache.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/cache/NodeDocumentCache.java (revision 1749862) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/cache/NodeDocumentCache.java (working copy) @@ -105,23 +105,25 @@ } /** - * Invalidate document with given keys iff their mod counts are different as - * passed in the map. + * Invalidate document with given keys iff their modification stamps are + * different as passed in the map. * - * @param modCounts map where key is the document id and the value is the mod count + * @param modStamps map where key is the document id and the value is the + * modification stamps. * @return number of invalidated entries */ @Nonnegative - public int invalidateOutdated(@Nonnull Map modCounts) { + public int invalidateOutdated(@Nonnull Map modStamps) { int invalidatedCount = 0; - for (Entry e : modCounts.entrySet()) { + for (Entry e : modStamps.entrySet()) { String id = e.getKey(); - Long modCount = e.getValue(); + ModificationStamp stamp = e.getValue(); NodeDocument doc = getIfPresent(id); if (doc == null) { continue; } - if (!Objects.equal(modCount, doc.getModCount())) { + if (!Objects.equal(stamp.modCount, doc.getModCount()) + || !Objects.equal(stamp.modified, doc.getModified())) { invalidate(id); invalidatedCount++; } Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java (revision 1749862) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java (working copy) @@ -69,6 +69,7 @@ import org.apache.jackrabbit.oak.plugins.document.UpdateUtils; import org.apache.jackrabbit.oak.plugins.document.cache.CacheChangesTracker; import org.apache.jackrabbit.oak.plugins.document.cache.CacheInvalidationStats; +import org.apache.jackrabbit.oak.plugins.document.cache.ModificationStamp; import org.apache.jackrabbit.oak.plugins.document.cache.NodeDocumentCache; import org.apache.jackrabbit.oak.plugins.document.locks.NodeDocumentLocks; import org.apache.jackrabbit.oak.plugins.document.locks.StripedNodeDocumentLocks; @@ -309,11 +310,11 @@ ids.size(), size); } - Map modCounts = getModCounts(ids); + Map modStamps = getModStamps(ids); result.queryCount++; - int invalidated = nodesCache.invalidateOutdated(modCounts); - for (String id : filter(ids, not(in(modCounts.keySet())))) { + int invalidated = nodesCache.invalidateOutdated(modStamps); + for (String id : filter(ids, not(in(modStamps.keySet())))) { nodesCache.invalidate(id); invalidated++; } @@ -1139,17 +1140,17 @@ try { dbCollection.update(query.get(), update, false, true); if (collection == Collection.NODES) { - Map modCounts = getModCounts(filterValues(cachedDocs, notNull()).keySet()); + Map modCounts = getModStamps(filterValues(cachedDocs, notNull()).keySet()); // update cache for (Entry entry : cachedDocs.entrySet()) { // the cachedDocs is not empty, so the collection = NODES Lock lock = nodeLocks.acquire(entry.getKey()); try { - Long postUpdateModCount = modCounts.get(entry.getKey()); - if (postUpdateModCount != null + ModificationStamp postUpdateModStamp = modCounts.get(entry.getKey()); + if (postUpdateModStamp != null && entry.getValue() != null && entry.getValue() != NodeDocument.NULL - && Long.valueOf(postUpdateModCount - 1).equals(entry.getValue().getModCount())) { + && Long.valueOf(postUpdateModStamp.modCount - 1).equals(entry.getValue().getModCount())) { // post update modCount is one higher than // what we currently see in the cache. we can // replace the cached document @@ -1179,30 +1180,40 @@ } /** - * Returns the {@link Document#MOD_COUNT} value of the documents with the + * Returns the {@link Document#MOD_COUNT} and + * {@link NodeDocument#MODIFIED_IN_SECS} values of the documents with the * given {@code keys}. The returned map will only contain entries for - * existing documents. + * existing documents. The default value is -1 if the document does not have + * a modCount field. The same applies to the modified field. * * @param keys the keys of the documents. - * @return map with key to {@link Document#MOD_COUNT} value mapping. + * @return map with key to modification stamp mapping. * @throws MongoException if the call fails */ @Nonnull - private Map getModCounts(Iterable keys) + private Map getModStamps(Iterable keys) throws MongoException { QueryBuilder query = QueryBuilder.start(Document.ID).in(keys); // Fetch only the modCount and id final BasicDBObject fields = new BasicDBObject(Document.ID, 1); fields.put(Document.MOD_COUNT, 1); + fields.put(NodeDocument.MODIFIED_IN_SECS, 1); DBCursor cursor = nodes.find(query.get(), fields); cursor.setReadPreference(ReadPreference.primary()); - Map modCounts = Maps.newHashMap(); + Map modCounts = Maps.newHashMap(); for (DBObject obj : cursor) { String id = (String) obj.get(Document.ID); Long modCount = Utils.asLong((Number) obj.get(Document.MOD_COUNT)); - modCounts.put(id, modCount); + if (modCount == null) { + modCount = -1L; + } + Long modified = Utils.asLong((Number) obj.get(NodeDocument.MODIFIED_IN_SECS)); + if (modified == null) { + modified = -1L; + } + modCounts.put(id, new ModificationStamp(modCount, modified)); } return modCounts; } Index: oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ResurrectNodeAfterRevisionGCTest.java =================================================================== --- oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ResurrectNodeAfterRevisionGCTest.java (revision 1749862) +++ oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/ResurrectNodeAfterRevisionGCTest.java (working copy) @@ -30,7 +30,6 @@ import org.apache.jackrabbit.oak.stats.Clock; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import static org.apache.jackrabbit.oak.plugins.document.util.Utils.getIdFromPath; @@ -39,8 +38,8 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeFalse; -@Ignore public class ResurrectNodeAfterRevisionGCTest extends AbstractMultiDocumentStoreTest { @@ -50,6 +49,7 @@ public ResurrectNodeAfterRevisionGCTest(DocumentStoreFixture dsf) { super(dsf); + assumeFalse(dsf instanceof DocumentStoreFixture.RDBFixture); } @Before @@ -66,6 +66,7 @@ } c = new Clock.Virtual(); c.waitUntil(System.currentTimeMillis()); + Revision.setClock(c); ns1 = new DocumentMK.Builder().setAsyncDelay(0) .clock(c).setClusterId(1).setDocumentStore(ds1).getNodeStore(); ns2 = new DocumentMK.Builder().setAsyncDelay(0) @@ -76,6 +77,7 @@ public void disposeNodeStores() { ns1.dispose(); ns2.dispose(); + Revision.resetClockToDefault(); } @Test