Commits

dr...@81dbac14-341a-0410-aa85-cbcd92e6f43e  committed c0dcdef

Submitted by: Andres March
Reviewed by: Not yet reviewed

  • Participants
  • Parent commits 4d6a11d

Comments (0)

Files changed (17)

         <echo message="Running tests with disk and memory caches" level="info"/>
         <antcall target="test-base"/>
         <antcall target="test-web"/>
-
+	
+       	<!-- ReRun tests using Memory and Disk Overflow Cache -->	
+		<copy file="${src.plugins}/diskpersistence/test/oscacheMemoryAndOverflowToDisk.properties" tofile="${build.test.dir}/oscache.properties" overwrite="yes"/>
+	    <echo message="Running tests with memory and disk overflow caches" level="info"/>
+	    <antcall target="test-base"/>
+	    <antcall target="test-web"/>
+        	
         <!-- Run clustering tests -->
         <echo message="Running tests with memory caches and clustering" level="info"/>
         <antcall target="test-cluster"/>

File docs/configuration.html

 <p><code>cache.path=/opt/myapp/cache</code></p>
 </blockquote>
 
+<h3>cache.persistence.overflow.only</h3>
+<p>Indicates whether the persistence should only happen once the memory cache capacity has been reached. 
+The default value is <code>false</code> for backwards compatibility but the recommended value is 
+<CODE>true</CODE> when the memory cache is enabled.  This drastically changes the behavior of the cache in that 
+the persisted cache will now be different then what is in memory.</p>
+
 <h3>cache.event.listeners</h3>
 <p>This takes a comma-delimited list of fully-qualified class names. Each class in the list <em>must</em>
 implement one (or more) of the following interfaces:

File docs/features.html

     expired the cache file will be used. This also gives fault tolerance if the
     server crashes.<br>
   </li>
+  <li>Persistence can also be switched to overflow mode using the property 
+  oscache.persistence.overflow.only.  This changes the default behavior   
+  (of persisting every cache entry when there is a listener) to only persist when the 
+  memory cache capacity has been reached.<br>
+  </li>
 </ul>
 
 <p><b>Excellent Performance</b></p>

File src/core/java/com/opensymphony/oscache/base/AbstractCacheAdministrator.java

     public static final String PERSISTENCE_CLASS_KEY = "cache.persistence.class";
 
     /**
+     * A String cache configuration property that specifies if the cache persistence
+     * will only be used in overflow mode, that is, when the memory cache capacity has been reached.
+     */
+    private static final String CACHE_PERSISTENCE_OVERFLOW_KEY = "cache.persistence.overflow.only";
+
+    /**
      * A String cache configuration property that holds a comma-delimited list of
      * classnames. These classes specify the event handlers that are to be applied
      * to the cache.
      */
     public static final String CACHE_ENTRY_EVENT_LISTENERS_KEY = "cache.event.listeners";
-
     protected Config config = null;
 
     /**
     private boolean memoryCaching = true;
 
     /**
+     * Whether the persistent cache should be used immediately or only when the memory capacity
+         * has been reached, ie. overflow only.
+     * This can be set via the {@link #CACHE_PERSISTENCE_OVERFLOW_KEY} configuration property.
+     */
+    private boolean overflowPersistence;
+
+    /**
      * Whether the disk cache should be unlimited in size, or matched 1-1 to the memory cache.
      * This can be set via the {@link #CACHE_DISK_UNLIMITED_KEY} configuration property.
      */
     }
 
     /**
+     * Check if we use overflowPersistence
+     *
+     * @return Returns the overflowPersistence.
+     */
+    public boolean isOverflowPersistence() {
+        return this.overflowPersistence;
+    }
+
+    /**
+     * Sets the overflowPersistence flag
+     *
+     * @param overflowPersistence The overflowPersistence to set.
+     */
+    public void setOverflowPersistence(boolean overflowPersistence) {
+        this.overflowPersistence = overflowPersistence;
+    }
+
+    /**
      * Retrieves an array containing instances all of the {@link CacheEventListener}
      * classes that are specified in the OSCache configuration file.
      */
         }
 
         unlimitedDiskCache = Boolean.valueOf(config.getProperty(CACHE_DISK_UNLIMITED_KEY)).booleanValue();
+        overflowPersistence = Boolean.valueOf(config.getProperty(CACHE_PERSISTENCE_OVERFLOW_KEY)).booleanValue();
 
         String cacheSize = getProperty(CACHE_CAPACITY_KEY);
 

File src/core/java/com/opensymphony/oscache/base/Cache.java

      *
      * @param useMemoryCaching Specify if the memory caching is going to be used
      * @param unlimitedDiskCache Specify if the disk caching is unlimited
+     * @param overflowPersistence Specify if the persistent cache is used in overflow only mode
      */
-    public Cache(boolean useMemoryCaching, boolean unlimitedDiskCache) {
-        this(useMemoryCaching, unlimitedDiskCache, false, null, 0);
+    public Cache(boolean useMemoryCaching, boolean unlimitedDiskCache, boolean overflowPersistence) {
+        this(useMemoryCaching, unlimitedDiskCache, overflowPersistence, false, null, 0);
     }
 
     /**
      * @see com.opensymphony.oscache.base.algorithm.UnlimitedCache
      * @param useMemoryCaching Specify if the memory caching is going to be used
      * @param unlimitedDiskCache Specify if the disk caching is unlimited
+     * @param overflowPersistence Specify if the persistent cache is used in overflow only mode
      * @param blocking This parameter takes effect when a cache entry has
      * just expired and several simultaneous requests try to retrieve it. While
      * one request is rebuilding the content, the other requests will either
      * @param algorithmClass The class implementing the desired algorithm
      * @param capacity The capacity
      */
-    public Cache(boolean useMemoryCaching, boolean unlimitedDiskCache, boolean blocking, String algorithmClass, int capacity) {
+    public Cache(boolean useMemoryCaching, boolean unlimitedDiskCache, boolean overflowPersistence, boolean blocking, String algorithmClass, int capacity) {
         // Instantiate the algo class if valid
         if (((algorithmClass != null) && (algorithmClass.length() > 0)) && (capacity > 0)) {
             try {
         }
 
         cacheMap.setUnlimitedDiskCache(unlimitedDiskCache);
+        cacheMap.setOverflowPersistence(overflowPersistence);
         cacheMap.setMemoryCaching(useMemoryCaching);
 
         this.blocking = blocking;

File src/core/java/com/opensymphony/oscache/base/NeedsRefreshException.java

  *
  * <p>January, 2004 - The OSCache developers are aware of the fact that throwing
  * an exception for a perfect valid situation (cache miss) is design smell. This will
- * be removed in the near future, and other means of refreshing the cache will be 
+ * be removed in the near future, and other means of refreshing the cache will be
  * provided.</p>
  *
  * @author        <a href="mailto:fbeauregard@pyxis-tech.com">Francois Beauregard</a>

File src/core/java/com/opensymphony/oscache/base/algorithm/AbstractConcurrentReadCache.java

     protected int threshold;
 
     /**
+     * Use overflow persistence caching.
+     */
+    private boolean overflowPersistence = false;
+
+    /**
      * Constructs a new, empty map with the specified initial capacity and the specified load factor.
      *
      * @param initialCapacity the initial capacity
     }
 
     /**
+     * Check if we use overflowPersistence
+     *
+     * @return Returns the overflowPersistence.
+     */
+    public boolean isOverflowPersistence() {
+        return this.overflowPersistence;
+    }
+
+    /**
+     * Sets the overflowPersistence flag
+     *
+     * @param overflowPersistence The overflowPersistence to set.
+     */
+    public void setOverflowPersistence(boolean overflowPersistence) {
+        this.overflowPersistence = overflowPersistence;
+    }
+
+    /**
      * Return the number of slots in this table.
      **/
     public synchronized int capacity() {
                 itemPut(key);
 
                 // Persist if required
-                if (persist) {
+                if (persist && !overflowPersistence) {
                     persistStore(key, value);
                 }
 
                     e.value = value;
                 }
 
-                if (persist) {
+                // Persist if required
+                if (persist && overflowPersistence) {
+                    persistRemove(key);
+                } else if (persist) {
                     persistStore(key, value);
                 }
 
                 count--;
 
                 /** OpenSymphony BEGIN */
-                if (!unlimitedDiskCache) {
+                if (!unlimitedDiskCache && !overflowPersistence) {
                     persistRemove(e.key);
                 }
 
+                if (overflowPersistence && ((size() + 1) >= maxEntries)) {
+                    persistStore(key, oldValue);
+                }
+
                 if (invokeAlgorithm) {
                     itemRemoved(key);
                 }
                         itemPut(key);
 
                         // Persist if required
-                        if (persist) {
+                        if (persist && !overflowPersistence) {
                             persistStore(key, value);
                         }
 
                             e.value = value;
                         }
 
-                        if (persist) {
+                        // Persist if required
+                        if (persist && overflowPersistence) {
+                            persistRemove(key);
+                        } else if (persist) {
                             persistStore(key, value);
                         }
 
                     count--;
 
                     /** OpenSymphony BEGIN */
-                    if (!unlimitedDiskCache) {
+                    if (!unlimitedDiskCache && !overflowPersistence) {
                         persistRemove(e.key);
                     }
 
+                    if (overflowPersistence && ((size() + 1) >= maxEntries)) {
+                        persistStore(key, oldValue);
+                    }
+
                     if (invokeAlgorithm) {
                         itemRemoved(key);
                     }

File src/core/java/com/opensymphony/oscache/extra/ScopeEventListenerImpl.java

  * @author <a href="mailto:abergevin@pyxis-tech.com">Alain Bergevin</a>
  */
 public class ScopeEventListenerImpl implements ScopeEventListener {
-
     /**
      * Scope names
      */
-    public static final String[] SCOPE_NAMES = {null, "page", "request", "session", "application"};
+    public static final String[] SCOPE_NAMES = {
+        null, "page", "request", "session", "application"
+    };
 
     /**
      * Number of known scopes

File src/core/java/com/opensymphony/oscache/general/GeneralCacheAdministrator.java

     private void createCache() {
         log.info("Creating new cache");
 
-        applicationCache = new Cache(isMemoryCaching(), isUnlimitedDiskCache(), isBlocking(), algorithmClass, cacheCapacity);
+        applicationCache = new Cache(isMemoryCaching(), isUnlimitedDiskCache(), isOverflowPersistence(), isBlocking(), algorithmClass, cacheCapacity);
 
         configureStandardListeners(applicationCache);
     }

File src/core/java/com/opensymphony/oscache/web/ServletCache.java

      * @param scope The scope of all entries in this hashmap
      */
     public ServletCache(ServletCacheAdministrator admin, int scope) {
-        super(admin.isMemoryCaching(), admin.isUnlimitedDiskCache());
+        super(admin.isMemoryCaching(), admin.isUnlimitedDiskCache(), admin.isOverflowPersistence());
         setScope(scope);
         this.admin = admin;
     }
      * @param scope The cache scope
      */
     public ServletCache(ServletCacheAdministrator admin, String algorithmClass, int limit, int scope) {
-        super(admin.isMemoryCaching(), admin.isUnlimitedDiskCache(), admin.isBlocking(), algorithmClass, limit);
+        super(admin.isMemoryCaching(), admin.isUnlimitedDiskCache(), admin.isOverflowPersistence(), admin.isBlocking(), algorithmClass, limit);
         setScope(scope);
         this.admin = admin;
     }

File src/core/test/com/opensymphony/oscache/base/algorithm/TestQueueCache.java

  */
 package com.opensymphony.oscache.base.algorithm;
 
+import com.opensymphony.oscache.base.Config;
+import com.opensymphony.oscache.base.persistence.PersistenceListener;
+import com.opensymphony.oscache.plugins.diskpersistence.DiskPersistenceListener;
+import com.opensymphony.oscache.plugins.diskpersistence.TestDiskPersistenceListener;
+
 import java.util.Iterator;
+import java.util.Properties;
 
 /**
  * Test class for the QueueCache class, which is the base class for FIFO
     }
 
     /**
+     * Test the put method with overflow parameter set
+     */
+    public void testPutOverflow() {
+        // Create a listener
+        PersistenceListener listener = new DiskPersistenceListener();
+
+        Properties p = new Properties();
+        p.setProperty("cache.path", TestDiskPersistenceListener.CACHEDIR);
+        p.setProperty("cache.memory", "true");
+        p.setProperty("cache.persistence.overflow.only", "true");
+        p.setProperty("cache.persistence.class", "com.opensymphony.oscache.plugins.diskpersistence.DiskPersistenceListener");
+        listener.configure(new Config(p));
+        getCache().setPersistenceListener(listener);
+        getCache().clear();
+        getCache().setMaxEntries(MAX_ENTRIES);
+        getCache().setOverflowPersistence(true);
+
+        if (getCache() instanceof UnlimitedCache) {
+            return; // nothing to test since memory will never overflow.
+        }
+
+        // Put elements in cache
+        for (int count = 0; count <= MAX_ENTRIES; count++) {
+            getCache().put(KEY + count, CONTENT + count);
+        }
+
+        try {
+            int numPersisted = 0;
+
+            // Check that number of elements persisted == 1 if it is an overflow cache or all
+            // if it is not overflow and writes every time.
+            for (int count = 0; count <= MAX_ENTRIES; count++) {
+                if (getCache().getPersistenceListener().isStored(KEY + count)) {
+                    numPersisted++;
+                }
+            }
+
+            if (getCache().isOverflowPersistence()) {
+                assertTrue("Only 1 element should have been persisted ", numPersisted == 1);
+            } else {
+                assertTrue("All elements should have been persisted ", numPersisted == (MAX_ENTRIES + 1));
+            }
+        } catch (Exception e) {
+            fail();
+        }
+
+        getCache().clear();
+    }
+
+    /**
      * Test the remove from cache
      */
     public void testRemove() {

File src/core/test/com/opensymphony/oscache/base/events/TestCacheEntryEvent.java

     public void testCacheEntryEvent() {
         // Create all the required objects
         GeneralCacheAdministrator admin = new GeneralCacheAdministrator();
-        Cache map = new Cache(admin.isMemoryCaching(), admin.isUnlimitedDiskCache());
+        Cache map = new Cache(admin.isMemoryCaching(), admin.isUnlimitedDiskCache(), admin.isOverflowPersistence());
         CacheEntry entry = new CacheEntry(KEY);
         CacheEntryEvent event = new CacheEntryEvent(map, entry, null);
 

File src/core/test/com/opensymphony/oscache/extra/TestCacheEntryEventListenerImpl.java

         // Construct the objects required for the tests
         CacheEntry entry = new CacheEntry(KEY);
         GeneralCacheAdministrator admin = new GeneralCacheAdministrator();
-        Cache cache = new Cache(admin.isMemoryCaching(), admin.isUnlimitedDiskCache());
+        Cache cache = new Cache(admin.isMemoryCaching(), admin.isUnlimitedDiskCache(), admin.isOverflowPersistence());
         CacheEntryEvent event = new CacheEntryEvent(cache, entry, null);
         CacheEntryEventListenerImpl listener = new CacheEntryEventListenerImpl();
 

File src/plugins/clustersupport/test/com/opensymphony/oscache/plugins/clustersupport/BaseTestBroadcastingListener.java

             listener = getListener();
             assertNotNull(listener);
 
-            cache = new Cache(true, false);
+            cache = new Cache(true, false, false);
             assertNotNull(cache);
 
             try {

File src/plugins/clustersupport/test/com/opensymphony/oscache/plugins/clustersupport/ListenForClusterTests.java

     private void initListeners() {
         BaseTestBroadcastingListener testcase = null;
         AbstractBroadcastingListener listener;
-        Cache cache = new Cache(true, false);
+        Cache cache = new Cache(true, false, false);
 
         // Add the JavaGroups listener
         try {

File src/plugins/diskpersistence/test/com/opensymphony/oscache/plugins/diskpersistence/TestDiskPersistenceListener.java

     /**
      * Cache dir to persist to
      */
-    private static final String CACHEDIR = "build/test/diskcache";
+    public static final String CACHEDIR = "build.test/diskcache";
 
     /**
      * The persistance listener used for the tests

File src/plugins/diskpersistence/test/oscacheMemoryAndOverflowToDisk.properties

+# CACHE IN MEMORY
+cache.memory=true
+
+# CACHE PERSISTENCE CLASS
+cache.persistence.class=com.opensymphony.oscache.plugins.diskpersistence.DiskPersistenceListener
+
+# CACHE DIRECTORY
+cache.path=/tmp/cachetagscache
+
+# CACHE OVERFLOW
+cache.persistence.overflow.only=true