Author: mduerig
Date: Thu Aug 18 15:18:11 2016
New Revision: 1756809
URL: http://svn.apache.org/viewvc?rev=1756809&view=rev
Log:
OAK-4675: SNFE thrown while testing FileStore.cleanup() running concurrently
with writes
* Synchronise FileAccess.Random.length as RandomAccessFile.length() is not
thread safe
* Test case
Credits to Andrei Dulceanu for the patch
Modified:
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileAccess.java
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/CompactionAndCleanupIT.java
Modified:
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileAccess.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileAccess.java?rev=1756809&r1=1756808&r2=1756809&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileAccess.java
(original)
+++
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileAccess.java
Thu Aug 18 15:18:11 2016
@@ -99,7 +99,7 @@ abstract class FileAccess {
}
@Override
- public int length() throws IOException {
+ public synchronized int length() throws IOException {
long length = file.length();
checkState(length < Integer.MAX_VALUE);
return (int) length;
Modified:
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/CompactionAndCleanupIT.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/CompactionAndCleanupIT.java?rev=1756809&r1=1756808&r2=1756809&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/CompactionAndCleanupIT.java
(original)
+++
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/CompactionAndCleanupIT.java
Thu Aug 18 15:18:11 2016
@@ -63,6 +63,7 @@ import org.apache.jackrabbit.oak.api.Com
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.commons.concurrent.ExecutorCloser;
+import org.apache.jackrabbit.oak.plugins.blob.ReferenceCollector;
import org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions;
import org.apache.jackrabbit.oak.segment.file.FileStore;
import org.apache.jackrabbit.oak.segment.file.FileStoreGCMonitor;
@@ -1042,6 +1043,70 @@ public class CompactionAndCleanupIT {
}
}
+ @Test
+ public void randomAccessFileConcurrentReadAndLength() throws Exception {
+ final FileStore fileStore = fileStoreBuilder(getFileStoreFolder())
+ .withGCOptions(defaultGCOptions().setRetainedGenerations(2))
+ .withMaxFileSize(1)
+ .withMemoryMapping(false)
+ .build();
+
+ final SegmentNodeStore nodeStore =
SegmentNodeStoreBuilders.builder(fileStore).build();
+ ExecutorService executorService = newFixedThreadPool(300);
+ final AtomicInteger counter = new AtomicInteger();
+ final ReferenceCollector dummyCollector = new ReferenceCollector() {
+
+ @Override
+ public void addReference(String reference, String nodeId) {
+ // do nothing
+ }
+ };
+
+ try {
+ Callable<Void> concurrentWriteTask = new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ NodeBuilder builder = nodeStore.getRoot().builder();
+ builder.setProperty("blob-" + counter.getAndIncrement(),
createBlob(nodeStore, 25 * 25));
+ nodeStore.merge(builder, EmptyHook.INSTANCE,
CommitInfo.EMPTY);
+ fileStore.flush();
+ return null;
+ }
+ };
+
+ Callable<Void> concurrentCleanupTask = new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ fileStore.cleanup();
+ return null;
+ }
+ };
+
+ Callable<Void> concurrentReferenceCollector = new Callable<Void>()
{
+ @Override
+ public Void call() throws Exception {
+ fileStore.collectBlobReferences(dummyCollector);
+ return null;
+ }
+ };
+
+ List<Future<?>> results = newArrayList();
+ for (int i = 0; i < 100; i++) {
+ results.add(executorService.submit(concurrentWriteTask));
+ results.add(executorService.submit(concurrentCleanupTask));
+
results.add(executorService.submit(concurrentReferenceCollector));
+ }
+
+ for (Future<?> result : results) {
+ assertNull(result.get());
+ }
+
+ } finally {
+ new ExecutorCloser(executorService).close();
+ fileStore.close();
+ }
+ }
+
private static void addContent(NodeBuilder builder) {
for (int k = 0; k < 10000; k++) {
builder.setProperty(UUID.randomUUID().toString(),
UUID.randomUUID().toString());