[infinispan-commits] Infinispan SVN: r869 - in trunk/cachestore/jdbm/src: test/java/org/infinispan/loaders/jdbm and 1 other directory.

infinispan-commits at lists.jboss.org infinispan-commits at lists.jboss.org
Mon Sep 28 06:50:57 EDT 2009


Author: galder.zamarreno at jboss.com
Date: 2009-09-28 06:50:57 -0400 (Mon, 28 Sep 2009)
New Revision: 869

Modified:
   trunk/cachestore/jdbm/src/main/java/org/infinispan/loaders/jdbm/JdbmCacheStore.java
   trunk/cachestore/jdbm/src/main/java/org/infinispan/loaders/jdbm/JdbmCacheStoreConfig.java
   trunk/cachestore/jdbm/src/test/java/org/infinispan/loaders/jdbm/JdbmCacheStoreTest.java
Log:
Additions to expiry trees are now done in the eviction thread based on queued up entries, so removed corresponding TODO.

Modified: trunk/cachestore/jdbm/src/main/java/org/infinispan/loaders/jdbm/JdbmCacheStore.java
===================================================================
--- trunk/cachestore/jdbm/src/main/java/org/infinispan/loaders/jdbm/JdbmCacheStore.java	2009-09-28 09:57:26 UTC (rev 868)
+++ trunk/cachestore/jdbm/src/main/java/org/infinispan/loaders/jdbm/JdbmCacheStore.java	2009-09-28 10:50:57 UTC (rev 869)
@@ -37,6 +37,8 @@
 import java.util.NoSuchElementException;
 import java.util.Properties;
 import java.util.Set;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
 
 /**
  * A persistent <code>CacheLoader</code> based on the JDBM project. See http://jdbm.sourceforge.net/ . Does not support
@@ -48,6 +50,7 @@
  * (key,"m") == meta and (key,"v") == value.
  *
  * @author Elias Ross
+ * @author Galder Zamarreño
  */
 @ThreadSafe
 public class JdbmCacheStore extends AbstractCacheStore {
@@ -59,7 +62,7 @@
    private static final String EXPIRY = "Expiry";
    private final String DATE = "HH:mm:ss.SSS";
 
-   private final Object expiryLock = new Object();
+   private BlockingQueue<ExpiryEntry> expiryEntryQueue;
 
    private JdbmCacheStoreConfig config;
    private RecordManager recman;
@@ -85,6 +88,8 @@
          locationStr = System.getProperty("java.io.tmpdir");
          config.setLocation(locationStr);
       }
+      
+      expiryEntryQueue = new LinkedBlockingQueue<ExpiryEntry>(config.getExpiryQueueSize());
 
       // JBCACHE-1448 db name parsing fix courtesy of Ciro Cavani
       /* Parse config string. */
@@ -275,24 +280,12 @@
       }
       Long at = new Long(expiry);
       Object key = entry.getKey();
-      if (trace)
-         log.trace("at " + new SimpleDateFormat(DATE).format(new Date(at)) + " expire " + key);
-      // TODO could store expiry entries in a separate thread; would remove this
-      // lock as well
-      synchronized (expiryLock) {
-         Object existing = expiryTree.insert(at, key, false);
-         if (existing != null) {
-            // in the case of collision make the key a List ...
-            if (existing instanceof List) {
-               ((List) existing).add(key);
-               expiryTree.insert(at, existing, true);
-            } else {
-               List<Object> al = new ArrayList<Object>(2);
-               al.add(existing);
-               al.add(key);
-               expiryTree.insert(at, al, true);
-            }
-         }
+      if (trace) log.trace("at " + new SimpleDateFormat(DATE).format(new Date(at)) + " expire " + key);
+      
+      try {
+         expiryEntryQueue.put(new ExpiryEntry(at, key));
+      } catch (InterruptedException e) {
+         Thread.currentThread().interrupt(); // Restore interruption status
       }
    }
 
@@ -356,26 +349,44 @@
     * @throws ClassNotFoundException 
     */
    private void purgeInternal0() throws Exception {
+      // Drain queue and update expiry tree
+      List<ExpiryEntry> entries = new ArrayList<ExpiryEntry>();
+      expiryEntryQueue.drainTo(entries);
+      for (ExpiryEntry entry : entries) {
+         Object existing = expiryTree.insert(entry.expiry, entry.key, false);
+         if (existing != null) {
+            // in the case of collision make the key a List ...
+            if (existing instanceof List) {
+               ((List) existing).add(entry.key);
+               expiryTree.insert(entry.expiry, existing, true);
+            } else {
+               List<Object> al = new ArrayList<Object>(2);
+               al.add(existing);
+               al.add(entry.key);
+               expiryTree.insert(entry.expiry, al, true);
+            }
+         }
+      }
+
+      // Browse the expiry and remove accordingly
       TupleBrowser browse = expiryTree.browse();
       Tuple tuple = new Tuple();
       List<Long> times = new ArrayList<Long>();
       List<Object> keys = new ArrayList<Object>();
-      synchronized (expiryLock) {
-         while (browse.getNext(tuple)) {
-            Long time = (Long) tuple.getKey();
-            if (time > System.currentTimeMillis())
-               break;
-            times.add(time);
-            Object key = tuple.getValue();
-            if (key instanceof List)
-               keys.addAll((List) key);
-            else
-               keys.add(key);
-         }
-         for (Long time : times) {
-            expiryTree.remove(time);
-         }
+      while (browse.getNext(tuple)) {
+         Long time = (Long) tuple.getKey();
+         if (time > System.currentTimeMillis())
+            break;
+         times.add(time);
+         Object key = tuple.getValue();
+         if (key instanceof List)
+            keys.addAll((List) key);
+         else
+            keys.add(key);
       }
+      for (Long time : times) {
+         expiryTree.remove(time);
+      }
 
       if (!keys.isEmpty())
          log.debug("purge (up to) " + keys.size() + " entries");
@@ -484,4 +495,14 @@
          return size;
       }
    }
+   
+   private final class ExpiryEntry {
+      private final Long expiry;
+      private final Object key;
+      
+      private ExpiryEntry(long expiry, Object key) {
+         this.expiry = new Long(expiry);
+         this.key = key;
+      }
+   }
 }
\ No newline at end of file

Modified: trunk/cachestore/jdbm/src/main/java/org/infinispan/loaders/jdbm/JdbmCacheStoreConfig.java
===================================================================
--- trunk/cachestore/jdbm/src/main/java/org/infinispan/loaders/jdbm/JdbmCacheStoreConfig.java	2009-09-28 09:57:26 UTC (rev 868)
+++ trunk/cachestore/jdbm/src/main/java/org/infinispan/loaders/jdbm/JdbmCacheStoreConfig.java	2009-09-28 10:50:57 UTC (rev 869)
@@ -3,67 +3,79 @@
 import java.util.Comparator;
 
 import org.infinispan.CacheException;
+import org.infinispan.config.Dynamic;
 import org.infinispan.loaders.LockSupportCacheStoreConfig;
-import org.infinispan.marshall.Marshaller;
 import org.infinispan.util.Util;
 
 /**
  * Configures {@link JdbmCacheStore}.
- * <p/>
- * <ul>
- * <li><tt>location</tt> - a location on disk where the store can write internal
- * files.</li>
- * <li><tt>comparatorClassName</tt> - comparator class used to sort the keys
- * by the cache loader. This should only need to be set when using keys that
- * do not have a natural ordering.
- * </ul>
  * 
  * @author Elias Ross
+ * @author Galder Zamarreño
  * @since 4.0
  */
 public class JdbmCacheStoreConfig extends LockSupportCacheStoreConfig {
 
-    private static final long serialVersionUID = 1L;
-    
-    String location = "jdbm";
-    String comparatorClassName = NaturalComparator.class.getName();
+   /** The serialVersionUID */
+   private static final long serialVersionUID = -3686035269816837880L;
+   /** 
+    * @configRef desc="A location on disk where the store can write internal files"
+    */
+   String location = "jdbm";
+   /** 
+    * @configRef desc="Comparator class used to sort the keys by the cache loader. 
+    * This should only need to be set when using keys that do not have a natural ordering."
+    */
+   String comparatorClassName = NaturalComparator.class.getName();
+   
+   /** 
+    * @configRef desc="Whenever a new entry is stored, an expiry entry is created and added 
+    * to the a queue that is later consumed by the eviction thread. This parameter sets the size
+    * of this queue."
+    */
+   @Dynamic
+   int expiryQueueSize = 10000;
 
-    public JdbmCacheStoreConfig() {
-        setCacheLoaderClassName(JdbmCacheStore.class.getName());
-    }
+   public JdbmCacheStoreConfig() {
+      setCacheLoaderClassName(JdbmCacheStore.class.getName());
+   }
 
-    public String getLocation() {
-        return location;
-    }
+   public String getLocation() {
+      return location;
+   }
 
-    public void setLocation(String location) {
-        testImmutability("location");
-        this.location = location;
-    }
+   public void setLocation(String location) {
+      testImmutability("location");
+      this.location = location;
+   }
 
-    /**
-     * Returns comparatorClassName.
-     */
-    public String getComparatorClassName() {
-        return comparatorClassName;
-    }
+   public String getComparatorClassName() {
+      return comparatorClassName;
+   }
 
-    /**
-     * Sets comparatorClassName.
-     */
-    public void setComparatorClassName(String comparatorClassName) {
-        this.comparatorClassName = comparatorClassName;
-    }
+   public void setComparatorClassName(String comparatorClassName) {
+      testImmutability("comparatorClassName");
+      this.comparatorClassName = comparatorClassName;
+   }
 
-    /**
-     * Returns a new comparator instance based on {@link #setComparatorClassName(String)}.
-     */
-    public Comparator createComparator() {
-        try {
-            return (Comparator) Util.getInstance(comparatorClassName);
-        } catch (Exception e) {
-            throw new CacheException(e);
-        }
-    }
+   public int getExpiryQueueSize() {
+      return expiryQueueSize;
+   }
 
+   public void setExpiryQueueSize(int expiryQueueSize) {
+      testImmutability("expiryQueueSize");
+      this.expiryQueueSize = expiryQueueSize;
+   }
+
+   /**
+    * Returns a new comparator instance based on {@link #setComparatorClassName(String)}.
+    */
+   public Comparator createComparator() {
+      try {
+         return (Comparator) Util.getInstance(comparatorClassName);
+      } catch (Exception e) {
+         throw new CacheException(e);
+      }
+   }
+
 }

Modified: trunk/cachestore/jdbm/src/test/java/org/infinispan/loaders/jdbm/JdbmCacheStoreTest.java
===================================================================
--- trunk/cachestore/jdbm/src/test/java/org/infinispan/loaders/jdbm/JdbmCacheStoreTest.java	2009-09-28 09:57:26 UTC (rev 868)
+++ trunk/cachestore/jdbm/src/test/java/org/infinispan/loaders/jdbm/JdbmCacheStoreTest.java	2009-09-28 10:50:57 UTC (rev 869)
@@ -70,7 +70,41 @@
       assert fcs.load("k2") == null;
       assert fcs.load("k3") == null;
    }
+   
+   public void testStopStartDoesntNukeValues() throws InterruptedException, CacheLoaderException {
+      assert !cs.containsKey("k1");
+      assert !cs.containsKey("k2");
 
+      long lifespan = 1;
+      long idle = 1;
+      InternalCacheEntry se1 = InternalEntryFactory.create("k1", "v1", lifespan);
+      InternalCacheEntry se2 = InternalEntryFactory.create("k2", "v2");
+      InternalCacheEntry se3 = InternalEntryFactory.create("k3", "v3", -1, idle);
+      InternalCacheEntry se4 = InternalEntryFactory.create("k4", "v4", lifespan, idle);
+
+      cs.store(se1);
+      cs.store(se2);
+      cs.store(se3);
+      cs.store(se4);
+      Thread.sleep(100);
+      // Force a purge expired so that expiry tree is updated
+      cs.purgeExpired();
+      cs.stop();
+      cs.start();
+      assert se1.isExpired();
+      assert cs.load("k1") == null;
+      assert !cs.containsKey("k1");
+      assert cs.load("k2") != null;
+      assert cs.containsKey("k2");
+      assert cs.load("k2").getValue().equals("v2");
+      assert se3.isExpired();
+      assert cs.load("k3") == null;
+      assert !cs.containsKey("k3");
+      assert se3.isExpired();
+      assert cs.load("k3") == null;
+      assert !cs.containsKey("k3");
+   }
+
    public void testIterator() throws Exception {
       InternalCacheEntry k1 = InternalEntryFactory.create("k1", "v1");
       InternalCacheEntry k2 = InternalEntryFactory.create("k2", "v2");



More information about the infinispan-commits mailing list