Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStore.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStore.java (revision 1711188) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStore.java (working copy) @@ -1148,7 +1148,7 @@ cachedDoc.markUpToDate(System.currentTimeMillis()); return castAsT(cachedDoc); } else { - return SR.fromRow(collection, row); + return convertFromDBObject(collection, row); } } } catch (Exception ex) { @@ -1972,11 +1972,16 @@ } } + @CheckForNull + protected T convertFromDBObject(@Nonnull Collection collection, @Nonnull RDBRow row) { + return SR.fromRow(collection, row); + } + private T runThroughCache(Collection collection, RDBRow row, long now) { if (collection != Collection.NODES) { // not in the cache anyway - return SR.fromRow(collection, row); + return convertFromDBObject(collection, row); } String id = row.getId(); @@ -1986,20 +1991,20 @@ // do not overwrite document in cache if the // existing one in the cache is newer - if (inCache != null && inCache != NodeDocument.NULL) { - // check mod count - Number cachedModCount = inCache.getModCount(); - if (cachedModCount == null) { - throw new IllegalStateException("Missing " + Document.MOD_COUNT); - } - if (modCount.longValue() <= cachedModCount.longValue()) { - // we can use the cached document - inCache.markUpToDate(now); - return castAsT(inCache); - } - } +// if (inCache != null && inCache != NodeDocument.NULL) { +// // check mod count +// Number cachedModCount = inCache.getModCount(); +// if (cachedModCount == null) { +// throw new IllegalStateException("Missing " + Document.MOD_COUNT); +// } +// if (modCount.longValue() <= cachedModCount.longValue()) { +// // we can use the cached document +// inCache.markUpToDate(now); +// return castAsT(inCache); +// } +// } - NodeDocument fresh = (NodeDocument) SR.fromRow(collection, row); + NodeDocument fresh = (NodeDocument) convertFromDBObject(collection, row); fresh.seal(); Lock lock = getAndLock(id); @@ -2039,5 +2044,8 @@ } return false; } - + + protected Cache getNodeDocumentCache() { + return nodesCache; + } } Index: oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/AbstractRDBConnectionTest.java =================================================================== --- oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/AbstractRDBConnectionTest.java (revision 0) +++ oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/AbstractRDBConnectionTest.java (working copy) @@ -0,0 +1,84 @@ +/* + * 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; + +import javax.sql.DataSource; + +import org.apache.jackrabbit.oak.plugins.document.rdb.RDBDataSourceFactory; +import org.apache.jackrabbit.oak.plugins.document.rdb.RDBOptions; +import org.apache.jackrabbit.oak.stats.Clock; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; + +/** + * Base class for test cases that need a {@link DataSource} + * to a clean test database. Tests in subclasses are automatically + * skipped if the configured database connection can not be created. + */ +public class AbstractRDBConnectionTest extends DocumentMKTestBase { + + protected DataSource dataSource; + protected DocumentMK mk; + + @BeforeClass + public static void checkRDBAvailable() { + } + + @Before + public void setUpConnection() throws Exception { + dataSource = RDBDataSourceFactory.forJdbcUrl("jdbc:h2:file:./target/h2-mk-test", "", ""); + Revision.setClock(getTestClock()); + mk = newBuilder(dataSource).open(); + } + + protected DocumentMK.Builder newBuilder(DataSource db) throws Exception { + RDBOptions opt = new RDBOptions().dropTablesOnClose(true).tablePrefix("mktest"); + return new DocumentMK.Builder().clock(getTestClock()).setRDBConnection(dataSource, opt); + } + + protected Clock getTestClock() throws InterruptedException { + return Clock.SIMPLE; + } + + @After + public void tearDownConnection() throws Exception { + mk.dispose(); + Revision.resetClockToDefault(); + } + + @Override + protected DocumentMK getDocumentMK() { + return mk; + } + + protected static byte[] readFully(DocumentMK mk, String blobId) { + int remaining = (int) mk.getLength(blobId); + byte[] bytes = new byte[remaining]; + + int offset = 0; + while (remaining > 0) { + int count = mk.read(blobId, offset, bytes, offset, remaining); + if (count < 0) { + break; + } + offset += count; + remaining -= count; + } + return bytes; + } +} Property changes on: oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/AbstractRDBConnectionTest.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CacheConsistencyRDBTest.java =================================================================== --- oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CacheConsistencyRDBTest.java (revision 0) +++ oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CacheConsistencyRDBTest.java (working copy) @@ -0,0 +1,133 @@ +/* + * 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; + +import static org.apache.jackrabbit.oak.plugins.document.Collection.NODES; +import static org.junit.Assert.assertTrue; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.sql.DataSource; + +import org.apache.jackrabbit.oak.plugins.document.DocumentMK.Builder; +import org.apache.jackrabbit.oak.plugins.document.rdb.RDBDataSourceFactory; +import org.apache.jackrabbit.oak.plugins.document.rdb.RDBDocumentStore; +import org.apache.jackrabbit.oak.plugins.document.rdb.RDBOptions; +import org.apache.jackrabbit.oak.plugins.document.rdb.RDBRow; +import org.apache.jackrabbit.oak.plugins.document.util.StringValue; +import org.apache.jackrabbit.oak.plugins.document.util.Utils; +import org.apache.jackrabbit.oak.spi.state.NodeState; +import org.junit.Before; +import org.junit.Test; + +import com.google.common.collect.Maps; + +public class CacheConsistencyRDBTest extends AbstractRDBConnectionTest { + + private TestStore store; + + @Before + @Override + public void setUpConnection() throws Exception { + dataSource = RDBDataSourceFactory.forJdbcUrl("jdbc:h2:file:./target/h2-mk-test-" + UUID.randomUUID(), "", ""); + DocumentMK.Builder builder = new DocumentMK.Builder() + .clock(getTestClock()).setAsyncDelay(0); + store = new TestStore(dataSource, builder, new RDBOptions().dropTablesOnClose(true)); + mk = builder.setDocumentStore(store).setLeaseCheck(false).open(); + } + + @Test + public void cacheConsistency() throws Exception { + mk.commit("/", "+\"node\":{}", null, null); + // add a child node. this will require an update + // of _lastRev on /node + mk.commit("/node", "+\"child\":{}", null, null); + // make sure the document is not cached + store.invalidateNodeDocument(Utils.getIdFromPath("/node")); + + Thread t = new Thread(new Runnable() { + @Override + public void run() { + store.query(NODES, + Utils.getKeyLowerLimit("/"), + Utils.getKeyUpperLimit("/"), 10); + } + }); + // block thread when it tries to convert db objects + store.semaphores.put(t, new Semaphore(0)); + t.start(); + + while (!store.semaphores.get(t).hasQueuedThreads()) { + Thread.sleep(10); + } + + final Semaphore done = new Semaphore(0); + new Thread(new Runnable() { + @Override + public void run() { + // trigger write back of _lastRevs + mk.runBackgroundOperations(); + done.release(); + } + }).start(); + + // wait at most one second for background thread + done.tryAcquire(1, TimeUnit.SECONDS); + + // release thread + store.semaphores.get(t).release(); + t.join(); + + NodeState root = mk.getNodeStore().getRoot(); + assertTrue(root.getChildNode("node").getChildNode("child").exists()); + } + + private static final class TestStore extends RDBDocumentStore { + + final Map semaphores = Maps.newConcurrentMap(); + + public TestStore(DataSource dataSource, Builder builder, RDBOptions opt) { + super(dataSource, builder, opt); + } + + @Override + protected T convertFromDBObject( + @Nonnull Collection collection, @Nullable RDBRow row) { + Semaphore s = semaphores.get(Thread.currentThread()); + if (s != null) { + s.acquireUninterruptibly(); + } + try { + return super.convertFromDBObject(collection, row); + } finally { + if (s != null) { + s.release(); + } + } + } + + public void invalidateNodeDocument(String key) { + getNodeDocumentCache().invalidate(new StringValue(key)); + } + } + +} Property changes on: oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CacheConsistencyRDBTest.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property