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());


Reply via email to