Author: mreutegg
Date: Wed Dec 14 14:50:15 2016
New Revision: 1774256

URL: http://svn.apache.org/viewvc?rev=1774256&view=rev
Log:
OAK-5238: IndexCopier causes concurrent update on NodeBuilder

Added:
    
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/directory/BufferedOakDirectory.java
   (with props)
    
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/directory/BufferedOakDirectoryTest.java
   (with props)
Modified:
    
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/OakDirectory.java
    
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/writer/DefaultIndexWriter.java
    
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/writer/IndexWriterUtils.java
    
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java
    
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/directory/CopyOnWriteDirectoryTest.java

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/OakDirectory.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/OakDirectory.java?rev=1774256&r1=1774255&r2=1774256&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/OakDirectory.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/OakDirectory.java
 Wed Dec 14 14:50:15 2016
@@ -67,6 +67,7 @@ import static org.apache.jackrabbit.JcrC
 import static org.apache.jackrabbit.oak.api.Type.BINARIES;
 import static org.apache.jackrabbit.oak.api.Type.STRINGS;
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.INDEX_DATA_CHILD_NAME;
+import static 
org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
 import static 
org.apache.jackrabbit.oak.plugins.memory.PropertyStates.createProperty;
 import static 
org.apache.jackrabbit.oak.spi.blob.BlobOptions.UploadType.SYNCHRONOUS;
 
@@ -252,6 +253,38 @@ public class OakDirectory extends Direct
         return "Directory for " + definition.getIndexName();
     }
 
+    /**
+     * Copies the file with the given {@code name} to the {@code dest}
+     * directory. The file is copied 'by reference'. That is, the file in the
+     * destination directory will reference the same blob values as the source
+     * file.
+     * <p>
+     * This method is a no-op if the file does not exist in this directory.
+     *
+     * @param dest the destination directory.
+     * @param name the name of the file to copy.
+     * @throws IOException if an error occurs while copying the file.
+     * @throws IllegalArgumentException if the destination directory does not
+     *          use the same {@link BlobFactory} as {@code this} directory.
+     */
+    public void copy(OakDirectory dest, String name)
+            throws IOException {
+        if (blobFactory != dest.blobFactory) {
+            throw new IllegalArgumentException("Source and destination " +
+                    "directory must reference the same BlobFactory");
+        }
+        NodeBuilder file = directoryBuilder.getChildNode(name);
+        if (file.exists()) {
+            // overwrite potentially already existing child
+            NodeBuilder destFile = dest.directoryBuilder.setChildNode(name, 
EMPTY_NODE);
+            for (PropertyState p : file.getProperties()) {
+                destFile.setProperty(p);
+            }
+            dest.fileNames.add(name);
+            dest.markDirty();
+        }
+    }
+
     public boolean isDirty() {
         return dirty;
     }

Added: 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/directory/BufferedOakDirectory.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/directory/BufferedOakDirectory.java?rev=1774256&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/directory/BufferedOakDirectory.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/directory/BufferedOakDirectory.java
 Wed Dec 14 14:50:15 2016
@@ -0,0 +1,220 @@
+/*
+ * 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.index.lucene.directory;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import com.google.common.collect.Sets;
+
+import org.apache.jackrabbit.oak.plugins.index.lucene.IndexDefinition;
+import org.apache.jackrabbit.oak.plugins.index.lucene.OakDirectory;
+import org.apache.jackrabbit.oak.plugins.index.lucene.OakDirectory.BlobFactory;
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.IOContext;
+import org.apache.lucene.store.IndexInput;
+import org.apache.lucene.store.IndexOutput;
+import org.apache.lucene.store.Lock;
+import org.apache.lucene.store.LockFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.Arrays.asList;
+import static 
org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
+import static 
org.apache.jackrabbit.oak.plugins.memory.ModifiedNodeState.squeeze;
+
+/**
+ * A directory implementation that buffers changes until {@link #close()},
+ * except for blob values. Those are written immediately to the store.
+ */
+public final class BufferedOakDirectory extends Directory {
+
+    static final int DELETE_THRESHOLD_UNTIL_REOPEN = 100;
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(BufferedOakDirectory.class);
+
+    private final BlobFactory blobFactory;
+
+    private final String dataNodeName;
+
+    private final IndexDefinition definition;
+
+    private final OakDirectory base;
+
+    private final Set<String> bufferedForDelete = Sets.newConcurrentHashSet();
+
+    private NodeBuilder bufferedBuilder = EMPTY_NODE.builder();
+
+    private OakDirectory buffered;
+
+    private int deleteCount;
+
+    public BufferedOakDirectory(@Nonnull NodeBuilder builder,
+                                @Nonnull String dataNodeName,
+                                @Nonnull IndexDefinition definition,
+                                @Nullable BlobStore blobStore) {
+        this.blobFactory = blobStore != null ?
+                new OakDirectory.BlobStoreBlobFactory(blobStore) :
+                new OakDirectory.NodeBuilderBlobFactory(builder);
+        this.dataNodeName = checkNotNull(dataNodeName);
+        this.definition = checkNotNull(definition);
+        this.base = new OakDirectory(checkNotNull(builder), dataNodeName,
+                definition, false, blobFactory);
+        reopenBuffered();
+    }
+
+    @Override
+    public String[] listAll() throws IOException {
+        LOG.debug("[{}]listAll()", definition.getIndexPath());
+        Set<String> all = Sets.newTreeSet();
+        all.addAll(asList(base.listAll()));
+        all.addAll(asList(buffered.listAll()));
+        all.removeAll(bufferedForDelete);
+        return all.toArray(new String[all.size()]);
+    }
+
+    @Override
+    public boolean fileExists(String name) throws IOException {
+        LOG.debug("[{}]fileExists({})", definition.getIndexPath(), name);
+        if (bufferedForDelete.contains(name)) {
+            return false;
+        }
+        return buffered.fileExists(name) || base.fileExists(name);
+    }
+
+    @Override
+    public void deleteFile(String name) throws IOException {
+        LOG.debug("[{}]deleteFile({})", definition.getIndexPath(), name);
+        if (base.fileExists(name)) {
+            bufferedForDelete.add(name);
+        }
+        if (buffered.fileExists(name)) {
+            buffered.deleteFile(name);
+            fileDeleted();
+        }
+    }
+
+    @Override
+    public long fileLength(String name) throws IOException {
+        LOG.debug("[{}]fileLength({})", definition.getIndexPath(), name);
+        if (bufferedForDelete.contains(name)) {
+            String msg = String.format("already deleted: [%s] %s",
+                    definition.getIndexPath(), name);
+            throw new FileNotFoundException(msg);
+        }
+        Directory dir = base;
+        if (buffered.fileExists(name)) {
+            dir = buffered;
+        }
+        return dir.fileLength(name);
+    }
+
+    @Override
+    public IndexOutput createOutput(String name, IOContext context)
+            throws IOException {
+        LOG.debug("[{}]createOutput({})", definition.getIndexPath(), name);
+        bufferedForDelete.remove(name);
+        return buffered.createOutput(name, context);
+    }
+
+    @Override
+    public void sync(Collection<String> names) throws IOException {
+        LOG.debug("[{}]sync({})", definition.getIndexPath(), names);
+        buffered.sync(names);
+        base.sync(names);
+    }
+
+    @Override
+    public IndexInput openInput(String name, IOContext context)
+            throws IOException {
+        LOG.debug("[{}]openInput({})", definition.getIndexPath(), name);
+        if (bufferedForDelete.contains(name)) {
+            String msg = String.format("already deleted: [%s] %s",
+                    definition.getIndexPath(), name);
+            throw new FileNotFoundException(msg);
+        }
+        Directory dir = base;
+        if (buffered.fileExists(name)) {
+            dir = buffered;
+        }
+        return dir.openInput(name, context);
+    }
+
+    @Override
+    public Lock makeLock(String name) {
+        return base.makeLock(name);
+    }
+
+    @Override
+    public void clearLock(String name) throws IOException {
+        base.clearLock(name);
+    }
+
+    @Override
+    public void close() throws IOException {
+        LOG.debug("[{}]close()", definition.getIndexPath());
+        buffered.close();
+        // copy buffered files to base
+        for (String name : buffered.listAll()) {
+            buffered.copy(base, name);
+        }
+        // remove files marked as deleted
+        for (String name : bufferedForDelete) {
+            base.deleteFile(name);
+        }
+        base.close();
+    }
+
+    @Override
+    public void setLockFactory(LockFactory lockFactory) throws IOException {
+        base.setLockFactory(lockFactory);
+    }
+
+    @Override
+    public LockFactory getLockFactory() {
+        return base.getLockFactory();
+    }
+
+    private void fileDeleted() throws IOException {
+        // get rid of non existing files once in a while
+        if (++deleteCount >= DELETE_THRESHOLD_UNTIL_REOPEN) {
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Reopen buffered OakDirectory. Current list of 
files: {}",
+                        Arrays.asList(buffered.listAll()));
+            }
+            buffered.close();
+            reopenBuffered();
+        }
+    }
+
+    private void reopenBuffered() {
+        // squeeze out child nodes marked as non existing
+        // those are files that were created and later deleted again
+        bufferedBuilder = squeeze(bufferedBuilder.getNodeState()).builder();
+        buffered = new OakDirectory(bufferedBuilder, dataNodeName,
+                definition, false, blobFactory);
+    }
+}

Propchange: 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/directory/BufferedOakDirectory.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/writer/DefaultIndexWriter.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/writer/DefaultIndexWriter.java?rev=1774256&r1=1774255&r2=1774256&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/writer/DefaultIndexWriter.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/writer/DefaultIndexWriter.java
 Wed Dec 14 14:50:15 2016
@@ -153,7 +153,7 @@ class DefaultIndexWriter implements Luce
     private IndexWriter getWriter() throws IOException {
         if (writer == null) {
             final long start = PERF_LOGGER.start();
-            directory = newIndexDirectory(definition, definitionBuilder, 
dirName, blobStore);
+            directory = newIndexDirectory(definition, definitionBuilder, 
dirName, indexCopier != null, blobStore);
             IndexWriterConfig config;
             if (indexCopier != null){
                 directory = indexCopier.wrapForWrite(definition, directory, 
reindex, dirName);

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/writer/IndexWriterUtils.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/writer/IndexWriterUtils.java?rev=1774256&r1=1774255&r2=1774256&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/writer/IndexWriterUtils.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/writer/IndexWriterUtils.java
 Wed Dec 14 14:50:15 2016
@@ -26,6 +26,7 @@ import java.util.Map;
 
 import javax.annotation.Nullable;
 
+import 
org.apache.jackrabbit.oak.plugins.index.lucene.directory.BufferedOakDirectory;
 import org.apache.jackrabbit.oak.plugins.index.lucene.FieldNames;
 import org.apache.jackrabbit.oak.plugins.index.lucene.IndexDefinition;
 import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants;
@@ -74,7 +75,8 @@ public class IndexWriterUtils {
     }
 
     public static Directory newIndexDirectory(IndexDefinition indexDefinition,
-            NodeBuilder definition, String dirName, @Nullable 
GarbageCollectableBlobStore blobStore)
+            NodeBuilder definition, String dirName, boolean buffered,
+            @Nullable GarbageCollectableBlobStore blobStore)
             throws IOException {
         String path = null;
         if (LuceneIndexConstants.PERSISTENCE_FILE.equalsIgnoreCase(
@@ -82,7 +84,11 @@ public class IndexWriterUtils {
             path = definition.getString(PERSISTENCE_PATH);
         }
         if (path == null) {
-            return new OakDirectory(definition, dirName, indexDefinition, 
false, blobStore);
+            if (buffered) {
+                return new BufferedOakDirectory(definition, dirName, 
indexDefinition, blobStore);
+            } else {
+                return new OakDirectory(definition, dirName, indexDefinition, 
false, blobStore);
+            }
         } else {
             // try {
             File file = new File(path);

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java?rev=1774256&r1=1774255&r2=1774256&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java
 Wed Dec 14 14:50:15 2016
@@ -371,13 +371,13 @@ public class LuceneIndexTest {
     }
 
     private void purgeDeletedDocs(NodeBuilder idx, IndexDefinition definition) 
throws IOException {
-        IndexWriter writer = new IndexWriter(newIndexDirectory(definition, 
idx, LuceneIndexConstants.INDEX_DATA_CHILD_NAME, null), 
getIndexWriterConfig(definition, true));
+        IndexWriter writer = new IndexWriter(newIndexDirectory(definition, 
idx, LuceneIndexConstants.INDEX_DATA_CHILD_NAME, false, null), 
getIndexWriterConfig(definition, true));
         writer.forceMergeDeletes();
         writer.close();
     }
 
     public int getDeletedDocCount(NodeBuilder idx, IndexDefinition definition) 
throws IOException {
-        IndexReader reader = 
DirectoryReader.open(newIndexDirectory(definition, idx, 
LuceneIndexConstants.INDEX_DATA_CHILD_NAME, null));
+        IndexReader reader = 
DirectoryReader.open(newIndexDirectory(definition, idx, 
LuceneIndexConstants.INDEX_DATA_CHILD_NAME, false, null));
         int numDeletes = reader.numDeletedDocs();
         reader.close();
         return numDeletes;

Added: 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/directory/BufferedOakDirectoryTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/directory/BufferedOakDirectoryTest.java?rev=1774256&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/directory/BufferedOakDirectoryTest.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/directory/BufferedOakDirectoryTest.java
 Wed Dec 14 14:50:15 2016
@@ -0,0 +1,180 @@
+/*
+ * 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.index.lucene.directory;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Random;
+import java.util.Set;
+
+import com.google.common.collect.Sets;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.jackrabbit.oak.plugins.index.lucene.IndexDefinition;
+import org.apache.jackrabbit.oak.plugins.index.lucene.OakDirectory;
+import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.IOContext;
+import org.apache.lucene.store.IndexInput;
+import org.apache.lucene.store.IndexOutput;
+import org.junit.Test;
+
+import static 
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.INDEX_DATA_CHILD_NAME;
+import static 
org.apache.jackrabbit.oak.plugins.index.lucene.directory.BufferedOakDirectory.DELETE_THRESHOLD_UNTIL_REOPEN;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class BufferedOakDirectoryTest {
+
+    private Random rnd = new Random();
+
+    private NodeState root = EmptyNodeState.EMPTY_NODE;
+
+    private NodeBuilder builder = root.builder();
+
+    @Test
+    public void createOutput() throws Exception {
+        Directory buffered = createDir(builder, true);
+        byte[] data = writeFile(buffered, "file");
+
+        // must not be visible yet in base
+        Directory base = createDir(builder, false);
+        assertFalse(base.fileExists("file"));
+        base.close();
+
+        buffered.close();
+
+        // now it must exist
+        base = createDir(builder, false);
+        assertFile(base, "file", data);
+        base.close();
+    }
+
+    @Test
+    public void listAll() throws Exception {
+        Directory buffered = createDir(builder, true);
+        writeFile(buffered, "file");
+
+        // must only show up after buffered is closed
+        Directory base = createDir(builder, false);
+        assertEquals(0, base.listAll().length);
+        base.close();
+        buffered.close();
+        base = createDir(builder, false);
+        assertEquals(Sets.newHashSet("file"), Sets.newHashSet(base.listAll()));
+        base.close();
+
+        buffered = createDir(builder, true);
+        buffered.deleteFile("file");
+        assertEquals(0, buffered.listAll().length);
+
+        // must only disappear after buffered is closed
+        base = createDir(builder, false);
+        assertEquals(Sets.newHashSet("file"), Sets.newHashSet(base.listAll()));
+        base.close();
+        buffered.close();
+        base = createDir(builder, false);
+        assertEquals(0, base.listAll().length);
+        base.close();
+    }
+
+    @Test
+    public void fileLength() throws Exception {
+        Directory base = createDir(builder, false);
+        writeFile(base, "file");
+        base.close();
+
+        Directory buffered = createDir(builder, true);
+        buffered.deleteFile("file");
+        try {
+            buffered.fileLength("file");
+            fail("must throw FileNotFoundException");
+        } catch (FileNotFoundException expected) {
+            // expected
+        }
+        try {
+            buffered.fileLength("unknown");
+            fail("must throw FileNotFoundException");
+        } catch (FileNotFoundException expected) {
+            // expected
+        }
+        buffered.close();
+    }
+
+    @Test
+    public void reopen() throws Exception {
+        Random rand = new Random(42);
+        Set<String> names = Sets.newHashSet();
+        Directory dir = createDir(builder, true);
+        for (int i = 0; i < 10 * DELETE_THRESHOLD_UNTIL_REOPEN; i++) {
+            String name = "file-" + i;
+            writeFile(dir, name);
+            if (rand.nextInt(20) != 0) {
+                dir.deleteFile(name);
+            } else {
+                // keep 5%
+                names.add(name);
+            }
+        }
+        assertEquals(names, Sets.newHashSet(dir.listAll()));
+        dir.close();
+
+        // open unbuffered and check list as well
+        dir = createDir(builder, false);
+        assertEquals(names, Sets.newHashSet(dir.listAll()));
+        dir.close();
+    }
+
+    private void assertFile(Directory dir, String file, byte[] expected)
+            throws IOException {
+        assertTrue(dir.fileExists(file));
+        assertEquals(expected.length, dir.fileLength(file));
+        IndexInput in = dir.openInput(file, IOContext.DEFAULT);
+        byte[] data = new byte[expected.length];
+        in.readBytes(data, 0, data.length);
+        in.close();
+        assertTrue(Arrays.equals(expected, data));
+    }
+
+    private Directory createDir(NodeBuilder builder, boolean buffered) {
+        IndexDefinition def = new IndexDefinition(root, 
builder.getNodeState(), "/foo");
+        if (buffered) {
+            return new BufferedOakDirectory(builder, INDEX_DATA_CHILD_NAME, 
def, null);
+        } else {
+            return new OakDirectory(builder, def,false);
+        }
+    }
+
+    private byte[] randomBytes(int size) {
+        byte[] data = new byte[size];
+        rnd.nextBytes(data);
+        return data;
+    }
+
+    private byte[] writeFile(Directory dir, String name) throws IOException {
+        byte[] data = randomBytes(rnd.nextInt((int) (16 * FileUtils.ONE_KB)));
+        IndexOutput out = dir.createOutput(name, IOContext.DEFAULT);
+        out.writeBytes(data, data.length);
+        out.close();
+        return data;
+    }
+}

Propchange: 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/directory/BufferedOakDirectoryTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/directory/CopyOnWriteDirectoryTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/directory/CopyOnWriteDirectoryTest.java?rev=1774256&r1=1774255&r2=1774256&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/directory/CopyOnWriteDirectoryTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/directory/CopyOnWriteDirectoryTest.java
 Wed Dec 14 14:50:15 2016
@@ -40,7 +40,6 @@ import org.apache.lucene.store.IOContext
 import org.apache.lucene.store.IndexOutput;
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
@@ -87,13 +86,12 @@ public class CopyOnWriteDirectoryTest {
     }
 
     // OAK-5238
-    @Ignore
     @Test
     public void copyOnWrite() throws Exception {
         IndexDefinition def = new IndexDefinition(ns.getRoot(), ns.getRoot(), 
"/foo");
         NodeBuilder builder = ns.getRoot().builder();
         Directory remote = IndexWriterUtils.newIndexDirectory(
-                def, builder.child("foo"), INDEX_DATA_CHILD_NAME, null);
+                def, builder.child("foo"), INDEX_DATA_CHILD_NAME, true, null);
         Directory dir = copier.wrapForWrite(def, remote, false, 
INDEX_DATA_CHILD_NAME);
         addFiles(dir);
         writeTree(builder);


Reply via email to