You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by th...@apache.org on 2015/03/05 16:21:14 UTC

svn commit: r1664377 - in /jackrabbit/oak/branches/1.0/oak-core/src: main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/ test/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/

Author: thomasm
Date: Thu Mar  5 15:21:13 2015
New Revision: 1664377

URL: http://svn.apache.org/r1664377
Log:
OAK-2571 Protect the persistent cache against Thread.interrupt

Added:
    jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/CacheMap.java
    jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/MapFactory.java
Modified:
    jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/BlobCache.java
    jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/MultiGenerationMap.java
    jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/NodeCache.java
    jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/PersistentCache.java
    jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/CacheTest.java

Modified: jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/BlobCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/BlobCache.java?rev=1664377&r1=1664376&r2=1664377&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/BlobCache.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/BlobCache.java Thu Mar  5 15:21:13 2015
@@ -20,7 +20,6 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
@@ -28,7 +27,7 @@ import javax.annotation.Nonnull;
 import org.apache.jackrabbit.oak.plugins.document.persistentCache.PersistentCache.GenerationCache;
 import org.apache.jackrabbit.oak.spi.blob.BlobStore;
 import org.apache.jackrabbit.oak.spi.blob.GarbageCollectableBlobStore;
-import org.h2.mvstore.MVMapConcurrent;
+import org.h2.mvstore.MVMap;
 import org.h2.mvstore.StreamStore;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -60,11 +59,11 @@ public class BlobCache implements BlobSt
     
     @Override
     public void addGeneration(int generation, boolean readOnly) {
-        Map<Long, byte[]> d = cache.openMap(generation, "data", 
-                new MVMapConcurrent.Builder<Long, byte[]>());
+        CacheMap<Long, byte[]> d = cache.openMap(generation, "data", 
+                new MVMap.Builder<Long, byte[]>());
         data.addReadMap(generation, d);
-        Map<String, byte[]> m = cache.openMap(generation, "meta", 
-                new MVMapConcurrent.Builder<String, byte[]>());
+        CacheMap<String, byte[]> m = cache.openMap(generation, "meta", 
+                new MVMap.Builder<String, byte[]>());
         meta.addReadMap(generation, m);
         if (!readOnly) {
             // the order is important:

Added: jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/CacheMap.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/CacheMap.java?rev=1664377&view=auto
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/CacheMap.java (added)
+++ jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/CacheMap.java Thu Mar  5 15:21:13 2015
@@ -0,0 +1,141 @@
+/*
+ * 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.persistentCache;
+
+import java.util.Map;
+
+import org.h2.mvstore.MVMap;
+import org.h2.mvstore.MVMap.Builder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A cache map. This map supports re-opening the store if this is needed.
+ *
+ * @param <K> the key type
+ * @param <V> the value type
+ */
+public class CacheMap<K, V> {
+    
+    static final Logger LOG = LoggerFactory.getLogger(CacheMap.class);
+    
+    private final MapFactory factory;
+    private final String name;
+    private final MVMap.Builder<K, V> builder;
+    private int openCount;
+    private volatile Map<K, V> map;
+    private volatile boolean closed;
+
+    
+    public CacheMap(MapFactory factory, String name, Builder<K, V> builder) {
+        this.factory = factory;
+        this.name = name;
+        this.builder = builder;
+        openMap();
+    }
+    
+    private void reopen(int i, Exception e) {
+        if (i > 10) {
+            LOG.warn("Too many re-opens; disabling this cache map", e);
+            closed = true;
+            return;
+        }
+        // clear the interrupt flag, to avoid re-opening many times
+        Thread.interrupted();
+        if (i == 0) {
+            LOG.warn("Re-opening map " + name, e);
+        } else {
+            LOG.debug("Re-opening map " + name + " again", e);
+            LOG.warn("Re-opening map " + name + " again");
+        }
+        openMap();
+    }
+    
+    public V put(K key, V value) {
+        for (int i = 0;; i++) {
+            if (closed) {
+                return null;
+            }
+            try {
+                return map.put(key, value);
+            } catch (Exception e) {
+                reopen(i, e);
+            }
+        }
+    }
+    
+    public V get(Object key) {
+        for (int i = 0;; i++) {
+            if (closed) {
+                return null;
+            }
+            try {
+                return map.get(key);
+            } catch (Exception e) {
+                reopen(i, e);
+            }
+        }
+    }
+    
+    public boolean containsKey(Object key) {
+        for (int i = 0;; i++) {
+            if (closed) {
+                return false;
+            }
+            try {
+                return map.containsKey(key);
+            } catch (Exception e) {
+                reopen(i, e);
+            }
+        }        
+    }
+    
+    public V remove(Object key) {
+        for (int i = 0;; i++) {
+            if (closed) {
+                return null;
+            }
+            try {
+                return map.remove(key);
+            } catch (Exception e) {
+                reopen(i, e);
+            }
+        }        
+    }
+
+    public void clear() {
+        for (int i = 0;; i++) {
+            if (closed) {
+                return;
+            }
+            try {
+                map.clear();
+            } catch (Exception e) {
+                reopen(i, e);
+            }
+        }        
+    }
+    
+    void openMap() {
+        openCount = factory.reopenStoreIfNeeded(openCount);
+        Map<K, V> m2 = factory.openMap(name, builder);
+        if (m2 != null) {
+            map = m2;
+        }
+    }
+    
+}

Added: jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/MapFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/MapFactory.java?rev=1664377&view=auto
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/MapFactory.java (added)
+++ jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/MapFactory.java Thu Mar  5 15:21:13 2015
@@ -0,0 +1,80 @@
+/*
+ * 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.persistentCache;
+
+import java.util.Map;
+
+import org.h2.mvstore.MVMap;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class MapFactory {
+    
+    static final Logger LOG = LoggerFactory.getLogger(MapFactory.class);
+
+    private int openCount = 1;
+
+    /**
+     * Ensure the store is open, re-opening it if needed. The store is first
+     * closed if the old open count if the old open count matches the current
+     * open count.
+     *
+     * @param oldOpenCount the old open count
+     * @return the new open count
+     */
+    synchronized int reopenStoreIfNeeded(int oldOpenCount) {
+        if (oldOpenCount == openCount) {
+            closeStore();
+            openCount++;
+            openStore();
+        }
+        return openCount;
+    }
+    
+    public int getOpenCount() {
+        return openCount;
+    }
+    
+    /**
+     * Open the store.
+     */
+    abstract void openStore();
+    
+    /**
+     * Close the store.
+     */
+    abstract void closeStore();
+    
+    /**
+     * Open or get the given map.
+     * 
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param name the map name
+     * @param builder the map builder
+     * @return
+     */
+    abstract <K, V> Map<K, V> openMap(String name, MVMap.Builder<K, V> builder);
+    
+    /**
+     * Get the file size in bytes.
+     * 
+     * @return the file size
+     */
+    abstract long getFileSize();
+
+}

Modified: jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/MultiGenerationMap.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/MultiGenerationMap.java?rev=1664377&r1=1664376&r2=1664377&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/MultiGenerationMap.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/MultiGenerationMap.java Thu Mar  5 15:21:13 2015
@@ -23,18 +23,18 @@ import java.util.concurrent.ConcurrentSk
 
 public class MultiGenerationMap<K, V> implements Map<K, V> {
     
-    private volatile Map<K, V> write;
-    private ConcurrentSkipListMap<Integer, Map<K, V>> read = 
-            new ConcurrentSkipListMap<Integer, Map<K, V>>();
+    private volatile CacheMap<K, V> write;
+    private ConcurrentSkipListMap<Integer, CacheMap<K, V>> read = 
+            new ConcurrentSkipListMap<Integer, CacheMap<K, V>>();
     
     MultiGenerationMap() {
     }
     
-    public void setWriteMap(Map<K, V> m) {
+    public void setWriteMap(CacheMap<K, V> m) {
         write = m;
     }
 
-    public void addReadMap(int generation, Map<K, V> m) {
+    public void addReadMap(int generation, CacheMap<K, V> m) {
         read.put(generation, m);
     }
     
@@ -51,7 +51,7 @@ public class MultiGenerationMap<K, V> im
     @Override
     public V get(Object key) {
         for (int generation : read.descendingKeySet()) {
-            Map<K, V> m = read.get(generation);
+            CacheMap<K, V> m = read.get(generation);
             if (m != null) {
                 V value = m.get(key);
                 if (value != null) {
@@ -68,7 +68,7 @@ public class MultiGenerationMap<K, V> im
     @Override
     public boolean containsKey(Object key) {
         for (int generation : read.descendingKeySet()) {
-            Map<K, V> m = read.get(generation);
+            CacheMap<K, V> m = read.get(generation);
             if (m != null) {
                 if (m.containsKey(key)) {
                     return true;

Modified: jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/NodeCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/NodeCache.java?rev=1664377&r1=1664376&r2=1664377&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/NodeCache.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/NodeCache.java Thu Mar  5 15:21:13 2015
@@ -26,7 +26,7 @@ import javax.annotation.Nullable;
 import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore;
 import org.apache.jackrabbit.oak.plugins.document.DocumentStore;
 import org.apache.jackrabbit.oak.plugins.document.persistentCache.PersistentCache.GenerationCache;
-import org.h2.mvstore.MVMapConcurrent;
+import org.h2.mvstore.MVMap;
 import org.h2.mvstore.type.DataType;
 
 import com.google.common.cache.Cache;
@@ -52,7 +52,7 @@ class NodeCache<K, V> implements Cache<K
         this.type = type;
         this.docNodeStore = docNodeStore;
         this.docStore = docStore;
-        PersistentCache.LOG.info("wrap " + this.type);
+        PersistentCache.LOG.info("wrapping map " + this.type);
         map = new MultiGenerationMap<K, V>();
     }
     
@@ -60,10 +60,10 @@ class NodeCache<K, V> implements Cache<K
     public void addGeneration(int generation, boolean readOnly) {
         DataType keyType = new KeyDataType(type);
         DataType valueType = new ValueDataType(docNodeStore, docStore, type);
-        MVMapConcurrent.Builder<K, V> b = new MVMapConcurrent.Builder<K, V>().
+        MVMap.Builder<K, V> b = new MVMap.Builder<K, V>().
                 keyType(keyType).valueType(valueType);
         String mapName = type.name();
-        Map<K, V> m = cache.openMap(generation, mapName, b);
+        CacheMap<K, V> m = cache.openMap(generation, mapName, b);
         map.addReadMap(generation, m);
         if (!readOnly) {
             map.setWriteMap(m);

Modified: jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/PersistentCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/PersistentCache.java?rev=1664377&r1=1664376&r2=1664377&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/PersistentCache.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/PersistentCache.java Thu Mar  5 15:21:13 2015
@@ -25,7 +25,8 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.plugins.document.DocumentStore;
 import org.apache.jackrabbit.oak.spi.blob.GarbageCollectableBlobStore;
 import org.h2.mvstore.FileStore;
-import org.h2.mvstore.MVMapConcurrent;
+import org.h2.mvstore.MVMap;
+import org.h2.mvstore.MVMap.Builder;
 import org.h2.mvstore.MVStore;
 import org.h2.mvstore.MVStoreTool;
 import org.slf4j.Logger;
@@ -54,8 +55,8 @@ public class PersistentCache {
             new ArrayList<GenerationCache>();
     
     private final String directory;
-    private MVStore writeStore;
-    private MVStore readStore;
+    private MapFactory writeStore;
+    private MapFactory readStore;
     private int maxSizeMB = 1024;
     private int readGeneration = -1;
     private int writeGeneration;
@@ -101,7 +102,7 @@ public class PersistentCache {
         if (dir.length() == 0) {
             readGeneration = -1;
             writeGeneration = 0;
-            writeStore = openStore(writeGeneration, false);
+            writeStore = createMapFactory(writeGeneration, false);
             return;
         }
         File dr = new File(dir);
@@ -139,9 +140,9 @@ public class PersistentCache {
         readGeneration = generations.size() > 1 ? generations.first() : -1;
         writeGeneration = generations.size() > 0 ? generations.last() : 0;
         if (readGeneration >= 0) {
-            readStore = openStore(readGeneration, true);
+            readStore = createMapFactory(readGeneration, true);
         }
-        writeStore = openStore(writeGeneration, false);
+        writeStore = createMapFactory(writeGeneration, false);
     }
     
     private String getFileName(int generation) {
@@ -151,54 +152,108 @@ public class PersistentCache {
         return directory + "/" + FILE_PREFIX + generation + FILE_SUFFIX;
     }
     
-    private MVStore openStore(int generation, boolean readOnly) {
-        String fileName = getFileName(generation);
-        MVStore.Builder builder = new MVStore.Builder();
-        if (compress) {
-            builder.compress();
-        }
-        if (fileName != null) {
-            builder.fileName(fileName);
-        }
-        if (readOnly) {
-            builder.readOnly();
-        }
-        if (maxSizeMB < 10) {
-            builder.cacheSize(maxSizeMB);
-        }
-        if (autoCompact >= 0) {
-            builder.autoCompactFillRate(autoCompact);
-        }
-        builder.backgroundExceptionHandler(new Thread.UncaughtExceptionHandler() {
+    private MapFactory createMapFactory(final int generation, final boolean readOnly) {
+        MapFactory f = new MapFactory() {
+            
+            final String fileName = getFileName(generation);
+            MVStore store;
+            
             @Override
-            public void uncaughtException(Thread t, Throwable e) {
-                LOG.error("Error in persistent cache", e);
+            void openStore() {
+                if (store != null) {
+                    return;
+                }
+                MVStore.Builder builder = new MVStore.Builder();
+                try {
+                    if (compress) {
+                        builder.compress();
+                    }
+                    if (fileName != null) {
+                        builder.fileName(fileName);
+                    }
+                    if (readOnly) {
+                        builder.readOnly();
+                    }
+                    if (maxSizeMB < 10) {
+                        builder.cacheSize(maxSizeMB);
+                    }
+                    if (autoCompact >= 0) {
+                        builder.autoCompactFillRate(autoCompact);
+                    }
+                    builder.backgroundExceptionHandler(new Thread.UncaughtExceptionHandler() {
+                        @Override
+                        public void uncaughtException(Thread t, Throwable e) {
+                            LOG.debug("Error in the background thread of the persistent cache", e);
+                            LOG.warn("Error in the background thread of the persistent cache: " + e);
+                        }
+                    });
+                    store = builder.open();
+                    if (appendOnly) {
+                        store.setReuseSpace(false);
+                    }
+                } catch (Exception e) {
+                    LOG.warn("Could not open the store " + fileName, e);
+                }
             }
-        });
-        MVStore store = builder.open();
-        if (appendOnly) {
-            store.setReuseSpace(false);
-        }
-        return store;
+            
+            @Override
+            synchronized void closeStore() {
+                if (store == null) {
+                    return;
+                }
+                try {
+                    boolean compact = compactOnClose;
+                    if (store.getFileStore().isReadOnly()) {
+                        compact = false;
+                    }
+                    store.close();
+                    if (compact) {
+                        MVStoreTool.compact(fileName, true);
+                    }
+                } catch (Exception e) {
+                    LOG.debug("Could not close or compact the store", e);
+                    LOG.warn("Could not close or compact the store: " + e);
+                }
+                store = null;
+            }
+
+            @Override
+            <K, V> Map<K, V> openMap(String name, Builder<K, V> builder) {
+                try {
+                    if (builder == null) {
+                        return store.openMap(name);
+                    }
+                    return store.openMap(name, builder);
+                } catch (Exception e) {
+                    LOG.warn("Could not open the map", e);
+                    return null;
+                }
+            }
+
+            @Override
+            long getFileSize() {
+                try {
+                    FileStore fs = store.getFileStore();
+                    if (fs == null) {
+                        return 0;
+                    }
+                    return fs.size();
+                } catch (Exception e) {
+                    LOG.warn("Could not retrieve the map size", e);
+                    return 0;
+                }
+            }
+        };
+        f.openStore();
+        return f;
     }
     
     public void close() {
-        closeStore(writeStore, writeGeneration);
-        closeStore(readStore, readGeneration);
-    }
-    
-    private void closeStore(MVStore s, int generation) {
-        if (s == null) {
-            return;
+        if (writeStore != null) {
+            writeStore.closeStore();
         }
-        String fileName = getFileName(generation);
-        boolean compact = compactOnClose;
-        if (s.getFileStore().isReadOnly()) {
-            compact = false;
-        }
-        s.close();
-        if (compact) {
-            MVStoreTool.compact(fileName, true);
+        if (readStore != null) {
+            readStore.closeStore();
         }
     }
     
@@ -250,9 +305,9 @@ public class PersistentCache {
         c.addGeneration(writeGeneration, false);
     }
     
-    synchronized <K, V> Map<K, V> openMap(int generation, String name, 
-            MVMapConcurrent.Builder<K, V> builder) {
-        MVStore s;
+    public synchronized <K, V> CacheMap<K, V> openMap(int generation, String name, 
+            MVMap.Builder<K, V> builder) {
+        MapFactory s;
         if (generation == readGeneration) {
             s = readStore;
         } else if (generation == writeGeneration) {
@@ -260,7 +315,7 @@ public class PersistentCache {
         } else {
             throw new IllegalArgumentException("Unknown generation: " + generation);
         }
-        return s.openMap(name, builder);
+        return new CacheMap<K, V>(s, name, builder);
     }
     
     public void switchGenerationIfNeeded() {
@@ -274,10 +329,10 @@ public class PersistentCache {
                 return;
             }
             int oldReadGeneration = readGeneration;
-            MVStore oldRead = readStore;
+            MapFactory oldRead = readStore;
             readStore = writeStore;
             readGeneration = writeGeneration;
-            MVStore w = openStore(writeGeneration + 1, false);
+            MapFactory w = createMapFactory(writeGeneration + 1, false);
             writeStore = w;
             writeGeneration++;
             for (GenerationCache c : caches) {
@@ -287,18 +342,14 @@ public class PersistentCache {
                 }
             }
             if (oldRead != null) {
-                oldRead.close();
+                oldRead.closeStore();
                 new File(getFileName(oldReadGeneration)).delete();
             }
         }
     }
     
     private boolean needSwitch() {
-        FileStore fs = writeStore.getFileStore();
-        if (fs == null) {
-            return false;
-        }
-        long size = fs.size();
+        long size = writeStore.getFileSize();
         if (size / 1024 / 1024 <= maxSizeMB) {
             return false;
         }
@@ -312,6 +363,11 @@ public class PersistentCache {
     public long getMaxBinaryEntrySize() {
         return maxBinaryEntry;
     }
+    
+    public int getOpenCount() {
+        return writeStore.getOpenCount();
+    }
+
 
     interface GenerationCache {
 

Modified: jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/CacheTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/CacheTest.java?rev=1664377&r1=1664376&r2=1664377&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/CacheTest.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/CacheTest.java Thu Mar  5 15:21:13 2015
@@ -18,9 +18,14 @@
  */
 package org.apache.jackrabbit.oak.plugins.document.persistentCache;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
 import java.io.ByteArrayInputStream;
+import java.io.File;
 import java.util.Random;
 
+import org.apache.commons.io.FileUtils;
 import org.apache.jackrabbit.oak.spi.blob.BlobStore;
 import org.apache.jackrabbit.oak.spi.blob.MemoryBlobStore;
 import org.junit.Test;
@@ -29,18 +34,62 @@ public class CacheTest {
 
     @Test
     public void test() throws Exception {
+        FileUtils.deleteDirectory(new File("target/cacheTest"));
+        PersistentCache cache = new PersistentCache("target/cacheTest,size=1,-compress");
+        try {
+            MemoryBlobStore mem = new MemoryBlobStore();
+            mem.setBlockSizeMin(100);
+            BlobStore b = cache.wrapBlobStore(mem);
+            Random r = new Random();
+            for (int i = 0; i < 10000; i++) {
+                byte[] data = new byte[100];
+                r.nextBytes(data);
+                String id = b.writeBlob(new ByteArrayInputStream(data));
+                b.readBlob(id, 0, new byte[1], 0, 1);
+            }
+        } finally {
+            cache.close();
+        }
+    }
+    
+    @Test
+    public void interrupt() throws Exception {
+        FileUtils.deleteDirectory(new File("target/cacheTest"));
         PersistentCache cache = new PersistentCache("target/cacheTest,size=1,-compress");
-        MemoryBlobStore mem = new MemoryBlobStore();
-        mem.setBlockSizeMin(100);
-        BlobStore b = cache.wrapBlobStore(mem);
-        Random r = new Random();
-        for(int i=0; i<10000; i++) {
-            byte[] data = new byte[100];
-            r.nextBytes(data);
-            String id = b.writeBlob(new ByteArrayInputStream(data));
-            b.readBlob(id, 0, new byte[1], 0, 1);
+        try {
+            CacheMap<String, String> m1 = cache.openMap(0, "m1", null);
+            CacheMap<String, String> m2 = cache.openMap(0, "test", null);
+            
+            // the cache file was opened once so far
+            assertEquals(1, cache.getOpenCount());
+            
+            // we store 20 mb of data, to ensure not all data is kept in memory
+            String largeString = new String(new char[1024 * 1024]);
+            int count = 10;
+            for (int i = 0; i < count; i++) {
+                m1.put("x" + i, largeString);
+                m2.put("y" + i, largeString);
+            }
+
+            // interrupt the thread, which will cause the FileChannel
+            // to be closed in the next read operation
+            Thread.currentThread().interrupt();
+
+            // this will force at least one read operation,
+            // which should re-open the maps
+            for (int i = 0; i < count; i++) {
+                m1.get("x" + i);
+                m2.get("y" + i);
+            }
+
+            assertEquals(2, cache.getOpenCount());
+
+            // re-opening will clear the interrupt flag
+            assertFalse(Thread.interrupted());
+            
+        } finally {
+            cache.close();
         }
-        cache.close();
     }
     
 }