Ok, I have checked in all core changes around expiry, so that you can now specify expiry:

(a) programmatically (per entry) or declaratively (cache-wide), and 
(b) absolutely (lifespan), relatively (idle time), or both.

See examples, etc on: 

http://www.jboss.org/community/docs/DOC-13449

So tests pass, and the core structure works well.  The only thing is, folks who have implemented CacheStores (Mircea and Adrian) may want to revisit your implementations.  Like I said tests pass, even the ones I added to BaseCacheStoreTest to test all forms of expiration, but there are probably optimisations you can do to prevent reading and deserializing expired state, only to have isExpired() return true and have to throw it away.

Cheers
Manik


Begin forwarded message:

Date: 25 March 2009 15:57:08 GMT
Subject: [horizon-commits] Horizon SVN: r40 - in trunk/src/test/java/org/horizon: expiry and 1 other directories.

Author: manik.surtani@jboss.com
Date: 2009-03-25 11:57:08 -0400 (Wed, 25 Mar 2009)
New Revision: 40

Modified:
  trunk/src/test/java/org/horizon/container/SimpleDataContainerTest.java
  trunk/src/test/java/org/horizon/expiry/ExpiryTest.java
  trunk/src/test/java/org/horizon/expiry/ReplicatedExpiryTest.java
  trunk/src/test/java/org/horizon/loader/BaseCacheStoreTest.java
Log:
Expiry can now be set either as an absolute lifespan or a maxIdle time

Modified: trunk/src/test/java/org/horizon/container/SimpleDataContainerTest.java
===================================================================
--- trunk/src/test/java/org/horizon/container/SimpleDataContainerTest.java 2009-03-25 12:43:13 UTC (rev 39)
+++ trunk/src/test/java/org/horizon/container/SimpleDataContainerTest.java 2009-03-25 15:57:08 UTC (rev 40)
@@ -4,6 +4,7 @@
import org.horizon.container.entries.InternalCacheEntry;
import org.horizon.container.entries.TransientCacheEntry;
import org.horizon.container.entries.MortalCacheEntry;
+import org.horizon.container.entries.ImmortalCacheEntry;

import java.util.HashSet;
import java.util.Set;
@@ -45,6 +46,40 @@
      assert dc.size() == 0;
   }

+   public void testUpdatingLastUsed() throws Exception {
+      long idle = 600000;
+      SimpleDataContainer dc = new SimpleDataContainer();
+      dc.put("k", "v", -1, -1);
+      InternalCacheEntry ice = dc.get("k");
+      assert ice instanceof ImmortalCacheEntry;
+      assert ice.getExpiryTime() == -1;
+      assert ice.getLastUsed() == -1;
+      assert ice.getCreated() == -1;
+      assert ice.getMaxIdle() == -1;
+      assert ice.getLifespan() == -1;
+      dc.put("k", "v", -1, idle);
+      long oldTime = System.currentTimeMillis();
+      Thread.sleep(10); // for time calc granularity
+      ice = dc.get("k");
+      assert ice instanceof TransientCacheEntry;
+      assert ice.getExpiryTime() == -1;
+      assert ice.getLastUsed() > oldTime;
+      Thread.sleep(10); // for time calc granularity
+      assert ice.getLastUsed() < System.currentTimeMillis();
+      assert ice.getMaxIdle() == idle;
+      assert ice.getCreated() == -1;
+      assert ice.getLifespan() == -1;
+
+      oldTime = System.currentTimeMillis();
+      Thread.sleep(10); // for time calc granularity
+      assert dc.containsKey("k");
+      
+      // check that the last used stamp has been updated on a get
+      assert ice.getLastUsed() > oldTime;
+      Thread.sleep(10); // for time calc granularity
+      assert ice.getLastUsed() < System.currentTimeMillis();
+   }
+
   public void testExpirableToImmortal() {
      SimpleDataContainer dc = new SimpleDataContainer();
      dc.put("k", "v", 6000000, -1);

Modified: trunk/src/test/java/org/horizon/expiry/ExpiryTest.java
===================================================================
--- trunk/src/test/java/org/horizon/expiry/ExpiryTest.java 2009-03-25 12:43:13 UTC (rev 39)
+++ trunk/src/test/java/org/horizon/expiry/ExpiryTest.java 2009-03-25 15:57:08 UTC (rev 40)
@@ -12,7 +12,7 @@

import java.util.HashMap;
import java.util.Map;
-import java.util.concurrent.TimeUnit;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;

@Test(groups = "functional", testName = "expiry.ExpiryTest")
public class ExpiryTest {
@@ -29,16 +29,17 @@
      TestingUtil.killCacheManagers(cm);
   }

-   public void testExpiryInPut() throws InterruptedException {
+   public void testLifespanExpiryInPut() throws InterruptedException {
      Cache cache = cm.getCache();
      long lifespan = 1000;
-      cache.put("k", "v", lifespan, TimeUnit.MILLISECONDS);
+      cache.put("k", "v", lifespan, MILLISECONDS);

      DataContainer dc = cache.getAdvancedCache().getDataContainer();
      InternalCacheEntry se = dc.get("k");
      assert se.getKey().equals("k");
      assert se.getValue().equals("v");
      assert se.getLifespan() == lifespan;
+      assert se.getMaxIdle() == -1;
      assert !se.isExpired();
      assert cache.get("k").equals("v");
      Thread.sleep(1100);
@@ -46,14 +47,32 @@
      assert cache.get("k") == null;
   }

-   public void testExpiryInPutAll() throws InterruptedException {
+   public void testIdleExpiryInPut() throws InterruptedException {
      Cache cache = cm.getCache();
+      long idleTime = 1000;
+      cache.put("k", "v", -1, MILLISECONDS, idleTime, MILLISECONDS);
+
+      DataContainer dc = cache.getAdvancedCache().getDataContainer();
+      InternalCacheEntry se = dc.get("k");
+      assert se.getKey().equals("k");
+      assert se.getValue().equals("v");
+      assert se.getLifespan() == -1;
+      assert se.getMaxIdle() == idleTime;
+      assert !se.isExpired();
+      assert cache.get("k").equals("v");
+      Thread.sleep(1100);
+      assert se.isExpired();
+      assert cache.get("k") == null;
+   }
+
+   public void testLifespanExpiryInPutAll() throws InterruptedException {
+      Cache cache = cm.getCache();
      long startTime = System.currentTimeMillis();
      long lifespan = 1000;
      Map m = new HashMap();
      m.put("k1", "v");
      m.put("k2", "v");
-      cache.putAll(m, lifespan, TimeUnit.MILLISECONDS);
+      cache.putAll(m, lifespan, MILLISECONDS);
      while (System.currentTimeMillis() < startTime + lifespan) {
         assert cache.get("k1").equals("v");
         assert cache.get("k2").equals("v");
@@ -68,11 +87,27 @@
      assert cache.get("k2") == null;
   }

-   public void testExpiryInPutIfAbsent() throws InterruptedException {
+   public void testIdleExpiryInPutAll() throws InterruptedException {
      Cache cache = cm.getCache();
+      long idleTime = 10000;
+      Map m = new HashMap();
+      m.put("k1", "v");
+      m.put("k2", "v");
+      cache.putAll(m, -1, MILLISECONDS, idleTime, MILLISECONDS);
+      assert cache.get("k1").equals("v");
+      assert cache.get("k2").equals("v");
+
+      Thread.sleep(idleTime + 100);
+
+      assert cache.get("k1") == null;
+      assert cache.get("k2") == null;
+   }
+
+   public void testLifespanExpiryInPutIfAbsent() throws InterruptedException {
+      Cache cache = cm.getCache();
      long startTime = System.currentTimeMillis();
      long lifespan = 1000;
-      assert cache.putIfAbsent("k", "v", lifespan, TimeUnit.MILLISECONDS) == null;
+      assert cache.putIfAbsent("k", "v", lifespan, MILLISECONDS) == null;
      while (System.currentTimeMillis() < startTime + lifespan) {
         assert cache.get("k").equals("v");
         Thread.sleep(50);
@@ -86,19 +121,33 @@
      assert cache.get("k") == null;

      cache.put("k", "v");
-      assert cache.putIfAbsent("k", "v", lifespan, TimeUnit.MILLISECONDS) != null;
+      assert cache.putIfAbsent("k", "v", lifespan, MILLISECONDS) != null;
   }

-   public void testExpiryInReplace() throws InterruptedException {
+   public void testIdleExpiryInPutIfAbsent() throws InterruptedException {
      Cache cache = cm.getCache();
+      long idleTime = 10000;
+      assert cache.putIfAbsent("k", "v", -1, MILLISECONDS, idleTime, MILLISECONDS) == null;
+      assert cache.get("k").equals("v");
+
+      Thread.sleep(idleTime + 100);
+
+      assert cache.get("k") == null;
+
+      cache.put("k", "v");
+      assert cache.putIfAbsent("k", "v", -1, MILLISECONDS, idleTime, MILLISECONDS) != null;
+   }
+
+   public void testLifespanExpiryInReplace() throws InterruptedException {
+      Cache cache = cm.getCache();
      long lifespan = 1000;
      assert cache.get("k") == null;
-      assert cache.replace("k", "v", lifespan, TimeUnit.MILLISECONDS) == null;
+      assert cache.replace("k", "v", lifespan, MILLISECONDS) == null;
      assert cache.get("k") == null;
      cache.put("k", "v-old");
      assert cache.get("k").equals("v-old");
      long startTime = System.currentTimeMillis();
-      assert cache.replace("k", "v", lifespan, TimeUnit.MILLISECONDS) != null;
+      assert cache.replace("k", "v", lifespan, MILLISECONDS) != null;
      assert cache.get("k").equals("v");
      while (System.currentTimeMillis() < startTime + lifespan) {
         assert cache.get("k").equals("v");
@@ -115,7 +164,7 @@

      startTime = System.currentTimeMillis();
      cache.put("k", "v");
-      assert cache.replace("k", "v", "v2", lifespan, TimeUnit.MILLISECONDS);
+      assert cache.replace("k", "v", "v2", lifespan, MILLISECONDS);
      while (System.currentTimeMillis() < startTime + lifespan) {
         assert cache.get("k").equals("v2");
         Thread.sleep(50);
@@ -128,4 +177,25 @@
      }
      assert cache.get("k") == null;
   }
+
+   public void testIdleExpiryInReplace() throws InterruptedException {
+      Cache cache = cm.getCache();
+      long idleTime = 10000;
+      assert cache.get("k") == null;
+      assert cache.replace("k", "v", -1, MILLISECONDS, idleTime, MILLISECONDS) == null;
+      assert cache.get("k") == null;
+      cache.put("k", "v-old");
+      assert cache.get("k").equals("v-old");
+      assert cache.replace("k", "v", -1, MILLISECONDS, idleTime, MILLISECONDS) != null;
+      assert cache.get("k").equals("v");
+
+      Thread.sleep(idleTime + 100);
+      assert cache.get("k") == null;
+
+      cache.put("k", "v");
+      assert cache.replace("k", "v", "v2", -1, MILLISECONDS, idleTime, MILLISECONDS);
+
+      Thread.sleep(idleTime + 100);
+      assert cache.get("k") == null;
+   }
}

Modified: trunk/src/test/java/org/horizon/expiry/ReplicatedExpiryTest.java
===================================================================
--- trunk/src/test/java/org/horizon/expiry/ReplicatedExpiryTest.java 2009-03-25 12:43:13 UTC (rev 39)
+++ trunk/src/test/java/org/horizon/expiry/ReplicatedExpiryTest.java 2009-03-25 15:57:08 UTC (rev 40)
@@ -1,12 +1,16 @@
package org.horizon.expiry;

import org.horizon.Cache;
+import org.horizon.container.entries.InternalCacheEntry;
+import org.horizon.container.entries.TransientMortalCacheEntry;
+import org.horizon.container.entries.MortalCacheEntry;
+import org.horizon.container.entries.TransientCacheEntry;
import org.horizon.config.Configuration;
import org.horizon.test.MultipleCacheManagersTest;
import org.testng.annotations.Test;

import java.util.List;
-import java.util.concurrent.TimeUnit;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;

@Test(groups = "functional", testName = "expiry.ReplicatedExpiryTest")
public class ReplicatedExpiryTest extends MultipleCacheManagersTest {
@@ -20,18 +24,33 @@
      c2 = caches.get(1);
   }

-   public void testExpiryReplicates() throws InterruptedException {
-      long start = System.currentTimeMillis();
+   public void testLifespanExpiryReplicates() throws InterruptedException {
      long lifespan = 3000;
-      c1.put("k", "v", lifespan, TimeUnit.MILLISECONDS);
+      c1.put("k", "v", lifespan, MILLISECONDS);
+      InternalCacheEntry ice = c2.getAdvancedCache().getDataContainer().get("k");

-      while (System.currentTimeMillis() < start + lifespan) {
-         assert c1.get("k").equals("v");
-         assert c2.get("k").equals("v");
-         Thread.sleep(250);
-      }
-      Thread.sleep(1000);
-      assert c1.get("k") == null;
-      assert c2.get("k") == null;
+      assert ice instanceof MortalCacheEntry;
+      assert ice.getLifespan() == lifespan;
+      assert ice.getMaxIdle() == -1;
   }
+
+   public void testIdleExpiryReplicates() throws InterruptedException {
+      long idle = 3000;
+      c1.put("k", "v", -1, MILLISECONDS, idle, MILLISECONDS);
+      InternalCacheEntry ice = c2.getAdvancedCache().getDataContainer().get("k");
+
+      assert ice instanceof TransientCacheEntry;
+      assert ice.getMaxIdle() == idle;
+      assert ice.getLifespan() == -1;
+   }
+
+   public void testBothExpiryReplicates() throws InterruptedException {
+      long lifespan = 10000;
+      long idle = 3000;
+      c1.put("k", "v", lifespan, MILLISECONDS, idle, MILLISECONDS);
+      InternalCacheEntry ice = c2.getAdvancedCache().getDataContainer().get("k");
+      assert ice instanceof TransientMortalCacheEntry;
+      assert ice.getLifespan() == lifespan;
+      assert ice.getMaxIdle() == idle;
+   }
}

Modified: trunk/src/test/java/org/horizon/loader/BaseCacheStoreTest.java
===================================================================
--- trunk/src/test/java/org/horizon/loader/BaseCacheStoreTest.java 2009-03-25 12:43:13 UTC (rev 39)
+++ trunk/src/test/java/org/horizon/loader/BaseCacheStoreTest.java 2009-03-25 15:57:08 UTC (rev 40)
@@ -84,49 +84,101 @@
      return new ObjectStreamMarshaller();
   }

-
-   public void testLoadAndStore() throws InterruptedException, CacheLoaderException {
+   public void testLoadAndStoreImmortal() throws InterruptedException, CacheLoaderException {
      assert !cs.containsKey("k");
-      InternalCacheEntry se = InternalEntryFactory.create("k", "v", -1, -1);
+      InternalCacheEntry se = InternalEntryFactory.create("k", "v");
      cs.store(se);

      assert cs.load("k").getValue().equals("v");
      assert cs.load("k").getLifespan() == -1;
+      assert cs.load("k").getMaxIdle() == -1;
      assert !cs.load("k").isExpired();
      assert cs.containsKey("k");
+   }

-      long now = System.currentTimeMillis();
+   public void testLoadAndStoreWithLifespan() throws InterruptedException, CacheLoaderException {
+      assert !cs.containsKey("k");
+
      long lifespan = 120000;
-      se = InternalEntryFactory.create("k", "v", lifespan);
+      InternalCacheEntry se = InternalEntryFactory.create("k", "v", lifespan);
      cs.store(se);

      assert cs.load("k").getValue().equals("v");
      assert cs.load("k").getLifespan() == lifespan;
+      assert cs.load("k").getMaxIdle() == -1;
      assert !cs.load("k").isExpired();
      assert cs.containsKey("k");

-      now = System.currentTimeMillis();
      lifespan = 1;
      se = InternalEntryFactory.create("k", "v", lifespan);
      cs.store(se);
-      Thread.sleep(100);
+      Thread.sleep(10);
      assert se.isExpired();
      assert cs.load("k") == null;
      assert !cs.containsKey("k");
   }

+   public void testLoadAndStoreWithIdle() throws InterruptedException, CacheLoaderException {
+      assert !cs.containsKey("k");
+
+      long idle = 120000;
+      InternalCacheEntry se = InternalEntryFactory.create("k", "v", -1, idle);
+      cs.store(se);
+
+      assert cs.load("k").getValue().equals("v");
+      assert cs.load("k").getLifespan() == -1;
+      assert cs.load("k").getMaxIdle() == idle;
+      assert !cs.load("k").isExpired();
+      assert cs.containsKey("k");
+
+      idle = 1;
+      se = InternalEntryFactory.create("k", "v", -1, idle);
+      cs.store(se);
+      Thread.sleep(10);
+      assert se.isExpired();
+      assert cs.load("k") == null;
+      assert !cs.containsKey("k");
+   }
+
+   public void testLoadAndStoreWithLifespanAndIdle() throws InterruptedException, CacheLoaderException {
+      assert !cs.containsKey("k");
+
+      long lifespan = 200000;
+      long idle = 120000;
+      InternalCacheEntry se = InternalEntryFactory.create("k", "v", lifespan, idle);
+      cs.store(se);
+
+      assert cs.load("k").getValue().equals("v");
+      assert cs.load("k").getLifespan() == lifespan;
+      assert cs.load("k").getMaxIdle() == idle;
+      assert !cs.load("k").isExpired();
+      assert cs.containsKey("k");
+
+      idle = 1;
+      se = InternalEntryFactory.create("k", "v", lifespan, idle);
+      cs.store(se);
+      Thread.sleep(10);
+      assert se.isExpired();
+      assert cs.load("k") == null;
+      assert !cs.containsKey("k");
+   }
+
   public void testStopStartDoesntNukeValues() throws InterruptedException, CacheLoaderException {
      assert !cs.containsKey("k1");
      assert !cs.containsKey("k2");

-      long now = System.currentTimeMillis();
      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);
-      Thread.sleep(100);
+      cs.store(se3);
+      cs.store(se4);
+      Thread.sleep(10);
      cs.stop();
      cs.start();
      assert se1.isExpired();
@@ -135,7 +187,12 @@
      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");
   }


@@ -246,11 +303,11 @@

   public void testRollbackFromADifferentThreadReusingTransactionKey() throws CacheLoaderException, InterruptedException {

-      cs.store(InternalEntryFactory.create("old", "old", -1, -1));
+      cs.store(InternalEntryFactory.create("old", "old"));

      List<Modification> mods = new ArrayList<Modification>();
-      mods.add(new Store(InternalEntryFactory.create("k1", "v1", -1, -1)));
-      mods.add(new Store(InternalEntryFactory.create("k2", "v2", -1, -1)));
+      mods.add(new Store(InternalEntryFactory.create("k1", "v1")));
+      mods.add(new Store(InternalEntryFactory.create("k2", "v2")));
      mods.add(new Remove("k1"));
      mods.add(new Remove("old"));
      final Transaction tx = EasyMock.createNiceMock(Transaction.class);
@@ -350,13 +407,14 @@

   public void testPurgeExpired() throws Exception {
      long lifespan = 1000;
+      long idle = 500;
      cs.store(InternalEntryFactory.create("k1", "v1", lifespan));
-      cs.store(InternalEntryFactory.create("k2", "v2", lifespan));
-      cs.store(InternalEntryFactory.create("k3", "v3", lifespan));
+      cs.store(InternalEntryFactory.create("k2", "v2", -1, idle));
+      cs.store(InternalEntryFactory.create("k3", "v3", lifespan, idle));
      assert cs.containsKey("k1");
      assert cs.containsKey("k2");
      assert cs.containsKey("k3");
-      Thread.sleep(lifespan + 100);
+      Thread.sleep(lifespan + 10);
      cs.purgeExpired();
      assert !cs.containsKey("k1");
      assert !cs.containsKey("k2");

_______________________________________________
horizon-commits mailing list
horizon-commits@lists.jboss.org
https://lists.jboss.org/mailman/listinfo/horizon-commits

--
Manik Surtani
Lead, JBoss Cache