Author: agilliland
Date: Tue Jan 17 15:25:02 2006
New Revision: 369957
URL: http://svn.apache.org/viewcvs?rev=369957&view=rev
Log:
switching to lazy (passive) cache expiration strategy. this eliminates the
overhead of iterating through the list of cache keys to find the right ones
purge and gives us a better chance at supporting Memcached, which doesn't
provide a way to get the list of cache keys.
Added:
incubator/roller/trunk/src/org/roller/presentation/cache/LazyExpiringCacheEntry.java
Modified:
incubator/roller/trunk/src/org/roller/presentation/cache/CacheManager.java
incubator/roller/trunk/src/org/roller/presentation/filters/FeedCacheFilter.java
incubator/roller/trunk/src/org/roller/presentation/filters/IfModifiedFeedCacheFilter.java
incubator/roller/trunk/src/org/roller/presentation/filters/WeblogPageCacheFilter.java
Modified:
incubator/roller/trunk/src/org/roller/presentation/cache/CacheManager.java
URL:
http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/cache/CacheManager.java?rev=369957&r1=369956&r2=369957&view=diff
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/cache/CacheManager.java
(original)
+++ incubator/roller/trunk/src/org/roller/presentation/cache/CacheManager.java
Tue Jan 17 15:25:02 2006
@@ -6,8 +6,10 @@
package org.roller.presentation.cache;
+import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
@@ -50,6 +52,9 @@
// a reference to the cache factory in use
private static CacheFactory mCacheFactory = null;
+ // maintain a cache of the last expired time for each weblog
+ private static Hashtable lastExpiredCache = null;
+
// a list of all cache handlers who have obtained a cache
private static Set cacheHandlers = new HashSet();
@@ -82,6 +87,9 @@
mLogger.info("Cache Manager Initialized.");
mLogger.info("Default cache factory =
"+mCacheFactory.getClass().getName());
+ // setup our cache for expiration dates
+ lastExpiredCache = new Hashtable();
+
// finally, add custom handlers
String customHandlers =
RollerConfig.getProperty("cache.customHandlers");
if(customHandlers != null && customHandlers.trim().length() > 0) {
@@ -190,6 +198,8 @@
mLogger.debug("invalidating entry = "+entry.getAnchor());
+ lastExpiredCache.put(entry.getWebsite().getHandle(), new Date());
+
Iterator handlers = cacheHandlers.iterator();
while(handlers.hasNext()) {
((CacheHandler) handlers.next()).invalidate(entry);
@@ -201,6 +211,8 @@
mLogger.debug("invalidating website = "+website.getHandle());
+ lastExpiredCache.put(website.getHandle(), new Date());
+
Iterator handlers = cacheHandlers.iterator();
while(handlers.hasNext()) {
((CacheHandler) handlers.next()).invalidate(website);
@@ -212,6 +224,8 @@
mLogger.debug("invalidating bookmark = "+bookmark.getId());
+ lastExpiredCache.put(bookmark.getWebsite().getHandle(), new Date());
+
Iterator handlers = cacheHandlers.iterator();
while(handlers.hasNext()) {
((CacheHandler) handlers.next()).invalidate(bookmark);
@@ -223,6 +237,8 @@
mLogger.debug("invalidating folder = "+folder.getId());
+ lastExpiredCache.put(folder.getWebsite().getHandle(), new Date());
+
Iterator handlers = cacheHandlers.iterator();
while(handlers.hasNext()) {
((CacheHandler) handlers.next()).invalidate(folder);
@@ -234,6 +250,8 @@
mLogger.debug("invalidating comment = "+comment.getId());
+
lastExpiredCache.put(comment.getWeblogEntry().getWebsite().getHandle(), new
Date());
+
Iterator handlers = cacheHandlers.iterator();
while(handlers.hasNext()) {
((CacheHandler) handlers.next()).invalidate(comment);
@@ -245,6 +263,8 @@
mLogger.debug("invalidating referer = "+referer.getId());
+ lastExpiredCache.put(referer.getWebsite().getHandle(), new Date());
+
Iterator handlers = cacheHandlers.iterator();
while(handlers.hasNext()) {
((CacheHandler) handlers.next()).invalidate(referer);
@@ -267,6 +287,8 @@
mLogger.debug("invalidating category = "+category.getId());
+ lastExpiredCache.put(category.getWebsite().getHandle(), new Date());
+
Iterator handlers = cacheHandlers.iterator();
while(handlers.hasNext()) {
((CacheHandler) handlers.next()).invalidate(category);
@@ -278,6 +300,8 @@
mLogger.debug("invalidating template = "+template.getId());
+ lastExpiredCache.put(template.getWebsite().getHandle(), new Date());
+
Iterator handlers = cacheHandlers.iterator();
while(handlers.hasNext()) {
((CacheHandler) handlers.next()).invalidate(template);
@@ -290,6 +314,14 @@
*/
public static void clear() {
+ // update all expired dates
+ Iterator it = lastExpiredCache.keySet().iterator();
+ String key = null;
+ while(it.hasNext()) {
+ key = (String) it.next();
+ lastExpiredCache.put(key, new Date());
+ }
+
// loop through all handlers and trigger a clear
CacheHandler handler = null;
Iterator handlers = cacheHandlers.iterator();
@@ -306,6 +338,14 @@
*/
public static void clear(String handlerClass) {
+ // update all expired dates
+ Iterator it = lastExpiredCache.keySet().iterator();
+ String key = null;
+ while(it.hasNext()) {
+ key = (String) it.next();
+ lastExpiredCache.put(key, new Date());
+ }
+
// loop through all handlers to find the one we want
CacheHandler handler = null;
Iterator handlers = cacheHandlers.iterator();
@@ -316,6 +356,14 @@
handler.clear();
}
}
+ }
+
+
+ /**
+ * Get the date of the last time the specified weblog was invalidated.
+ */
+ public static Date getLastExpiredDate(String weblogHandle) {
+ return (Date) lastExpiredCache.get(weblogHandle);
}
Added:
incubator/roller/trunk/src/org/roller/presentation/cache/LazyExpiringCacheEntry.java
URL:
http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/cache/LazyExpiringCacheEntry.java?rev=369957&view=auto
==============================================================================
---
incubator/roller/trunk/src/org/roller/presentation/cache/LazyExpiringCacheEntry.java
(added)
+++
incubator/roller/trunk/src/org/roller/presentation/cache/LazyExpiringCacheEntry.java
Tue Jan 17 15:25:02 2006
@@ -0,0 +1,73 @@
+/*
+ * LazyExpiringCacheEntry.java
+ *
+ * Created on January 17, 2006, 10:14 AM
+ */
+
+package org.roller.presentation.cache;
+
+import java.util.Date;
+
+/**
+ * A cache entry that is meant to expire in a lazy fashion.
+ *
+ * The way to use this class is to wrap the object you want to cache in an
+ * instance of this class and store that in your cache. Then when you want
+ * to retrieve this entry you must input a last-expired time which can be
+ * compared against the time this entry was cached to determine if the cached
+ * entry is "fresh". If the object is not fresh then we don't return it.
+ *
+ * This essentially allows us to track when an object is cached and then before
+ * we can retrieve that cached object we must compare it with it's last known
+ * invalidation time to make sure it hasn't expired. This is useful because
+ * instead of actively purging lots of cached objects from the cache at
+ * invalidation time, we can now be lazy and just invalidate them when we
+ * actually try to retrieve the cached object.
+ *
+ * This is useful for Roller because we will no longer have to iterate through
+ * the list of cached objects and inspect the keys to figure out what items to
+ * invalidate. Instead we can just sit back and let the items be invalidated
as
+ * we try to use them.
+ *
+ * @author Allen Gilliland
+ */
+public class LazyExpiringCacheEntry {
+
+ private Object value = null;
+ private long timeCached = -1;
+
+
+ public LazyExpiringCacheEntry(Object item) {
+ this.value = item;
+ this.timeCached = System.currentTimeMillis();
+ }
+
+
+ /**
+ * Retrieve the value of this cache entry if it is still "fresh".
+ *
+ * If the value has expired then we return null.
+ */
+ public Object getValue(long lastInvalidated) {
+ if(this.isInvalid(lastInvalidated)) {
+ return null;
+ } else {
+ return this.value;
+ }
+ }
+
+
+ /**
+ * Determine if this cache entry has expired.
+ */
+ public boolean isInvalid(long lastInvalidated) {
+
+ return (this.timeCached < lastInvalidated);
+ }
+
+
+ public long getTimeCached() {
+ return timeCached;
+ }
+
+}
Modified:
incubator/roller/trunk/src/org/roller/presentation/filters/FeedCacheFilter.java
URL:
http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/filters/FeedCacheFilter.java?rev=369957&r1=369956&r2=369957&view=diff
==============================================================================
---
incubator/roller/trunk/src/org/roller/presentation/filters/FeedCacheFilter.java
(original)
+++
incubator/roller/trunk/src/org/roller/presentation/filters/FeedCacheFilter.java
Tue Jan 17 15:25:02 2006
@@ -38,6 +38,7 @@
import org.roller.presentation.cache.Cache;
import org.roller.presentation.cache.CacheHandler;
import org.roller.presentation.cache.CacheManager;
+import org.roller.presentation.cache.LazyExpiringCacheEntry;
import org.roller.presentation.util.CacheHttpServletResponseWrapper;
import org.roller.presentation.util.ResponseContent;
@@ -91,10 +92,27 @@
return;
}
- String key = "feedCache:"+this.generateKey(feedRequest);
+ String key = this.CACHE_ID+":"+this.generateKey(feedRequest);
try {
- ResponseContent respContent = (ResponseContent)
this.mCache.get(key);
+ ResponseContent respContent = null;
+
+ // we need the last expiration time for the given weblog
+ long lastExpiration = 0;
+ Date lastExpirationDate =
+ (Date)
CacheManager.getLastExpiredDate(feedRequest.getWeblogHandle());
+ if(lastExpirationDate != null)
+ lastExpiration = lastExpirationDate.getTime();
+
+ LazyExpiringCacheEntry entry =
+ (LazyExpiringCacheEntry) this.mCache.get(key);
+ if(entry != null) {
+ respContent = (ResponseContent) entry.getValue(lastExpiration);
+
+ if(respContent == null)
+ mLogger.debug("HIT-INVALID "+key);
+ }
+
if (respContent == null) {
mLogger.debug("MISS "+key);
@@ -111,7 +129,7 @@
if (request.getAttribute("DisplayException") == null) {
ResponseContent rc = cacheResponse.getContent();
- this.mCache.put(key, rc);
+ this.mCache.put(key, new LazyExpiringCacheEntry(rc));
} else {
// it is expected that whoever caught this display
exception
// is the one who reported it to the logs
@@ -212,6 +230,7 @@
// - the planet feed
// - all weblog feeds
+ /*
Set removeSet = new HashSet();
// TODO: it would be nice to be able to do this without iterating
@@ -235,6 +254,7 @@
this.mCache.remove(removeSet);
this.purges += removeSet.size();
+ */
}
Modified:
incubator/roller/trunk/src/org/roller/presentation/filters/IfModifiedFeedCacheFilter.java
URL:
http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/filters/IfModifiedFeedCacheFilter.java?rev=369957&r1=369956&r2=369957&view=diff
==============================================================================
---
incubator/roller/trunk/src/org/roller/presentation/filters/IfModifiedFeedCacheFilter.java
(original)
+++
incubator/roller/trunk/src/org/roller/presentation/filters/IfModifiedFeedCacheFilter.java
Tue Jan 17 15:25:02 2006
@@ -44,12 +44,13 @@
import org.roller.presentation.cache.Cache;
import org.roller.presentation.cache.CacheHandler;
import org.roller.presentation.cache.CacheManager;
+import org.roller.presentation.cache.LazyExpiringCacheEntry;
/**
* A filter used for caching last modified dates.
*
- * This may be applied to /rss/*, /atom/*, /flavor/*, and /planetrss
+ * This may be applied to /rss/*, /atom/*, /flavor/*
*
* @web.filter name="IfModifiedFeedCacheFilter"
*
@@ -92,11 +93,25 @@
return;
}
- String key = "ifmod:"+this.generateKey(feedRequest);
+ String key = this.CACHE_ID+":"+this.generateKey(feedRequest);
Date updateTime = null;
try {
- updateTime = (Date) this.mCache.get(key);
+ // we need the last expiration time for the given weblog
+ long lastExpiration = 0;
+ Date lastExpirationDate =
+ (Date)
CacheManager.getLastExpiredDate(feedRequest.getWeblogHandle());
+ if(lastExpirationDate != null)
+ lastExpiration = lastExpirationDate.getTime();
+
+ LazyExpiringCacheEntry entry =
+ (LazyExpiringCacheEntry) this.mCache.get(key);
+ if(entry != null) {
+ updateTime = (Date) entry.getValue(lastExpiration);
+
+ if(updateTime == null)
+ mLogger.debug("HIT-INVALID "+key);
+ }
if (updateTime == null) {
mLogger.debug("MISS "+key);
@@ -110,7 +125,7 @@
umgr.getWebsiteByHandle(feedRequest.getWeblogHandle()),
feedRequest.getWeblogCategory());
- this.mCache.put(key, updateTime);
+ this.mCache.put(key, new
LazyExpiringCacheEntry(updateTime));
}
} else {
@@ -218,6 +233,7 @@
// - the planet feed
// - all weblog feeds
+ /*
Set removeSet = new HashSet();
// TODO: it would be nice to be able to do this without iterating
@@ -240,6 +256,7 @@
}
this.mCache.remove(removeSet);
+ */
}
Modified:
incubator/roller/trunk/src/org/roller/presentation/filters/WeblogPageCacheFilter.java
URL:
http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/filters/WeblogPageCacheFilter.java?rev=369957&r1=369956&r2=369957&view=diff
==============================================================================
---
incubator/roller/trunk/src/org/roller/presentation/filters/WeblogPageCacheFilter.java
(original)
+++
incubator/roller/trunk/src/org/roller/presentation/filters/WeblogPageCacheFilter.java
Tue Jan 17 15:25:02 2006
@@ -9,10 +9,7 @@
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
import java.util.Map;
-import java.util.Set;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
@@ -37,6 +34,7 @@
import org.roller.presentation.cache.Cache;
import org.roller.presentation.cache.CacheHandler;
import org.roller.presentation.cache.CacheManager;
+import org.roller.presentation.cache.LazyExpiringCacheEntry;
import org.roller.presentation.util.CacheHttpServletResponseWrapper;
import org.roller.presentation.util.ResponseContent;
@@ -50,8 +48,7 @@
*/
public class WeblogPageCacheFilter implements Filter, CacheHandler {
- private static Log mLogger =
- LogFactory.getFactory().getInstance(WeblogPageCacheFilter.class);
+ private static Log mLogger =
LogFactory.getLog(WeblogPageCacheFilter.class);
// a unique identifier for this cache, this is used as the prefix for
// roller config properties that apply to this cache
@@ -89,12 +86,26 @@
return;
}
- String key = "pageCache:"+this.generateKey(pageRequest);
+ String key = this.CACHE_ID+":"+this.generateKey(pageRequest);
try {
ResponseContent respContent = null;
if(!this.excludeOwnerPages || !pageRequest.isLoggedIn()) {
- respContent = (ResponseContent) this.mCache.get(key);
+ // we need the last expiration time for the given weblog
+ long lastExpiration = 0;
+ Date lastExpirationDate =
+ (Date)
CacheManager.getLastExpiredDate(pageRequest.getWeblogHandle());
+ if(lastExpirationDate != null)
+ lastExpiration = lastExpirationDate.getTime();
+
+ LazyExpiringCacheEntry entry =
+ (LazyExpiringCacheEntry) this.mCache.get(key);
+ if(entry != null) {
+ respContent = (ResponseContent)
entry.getValue(lastExpiration);
+
+ if(respContent == null)
+ mLogger.debug("HIT-INVALID "+key);
+ }
}
if (respContent == null) {
@@ -117,8 +128,8 @@
// only cache if this is not a logged in user?
if (!this.excludeOwnerPages || !pageRequest.isLoggedIn()) {
if (rc != null && rc.getSize() > 0) {
- this.mCache.put(key, rc);
- } else if (mLogger.isDebugEnabled()) {
+ this.mCache.put(key, new
LazyExpiringCacheEntry(rc));
+ } else {
mLogger.debug("Not caching zero length content for
key: " + key);
}
} else {
@@ -233,6 +244,7 @@
// - the weblog entry permalink page
// - the weblog archive pages
+ /*
String handle = entry.getWebsite().getHandle();
String keyBase = "pageCache:weblog/"+handle+"/page";
@@ -260,6 +272,9 @@
this.mCache.remove(removeSet);
this.purges += removeSet.size();
+ */
+
+ this.invalidate(entry.getWebsite());
}
@@ -270,6 +285,7 @@
mLogger.debug("invalidating website = "+website.getHandle());
+ /*
// we need to remove the following cached items if they exist
// - all pages for this weblog
@@ -292,6 +308,7 @@
this.mCache.remove(removeSet);
this.purges += removeSet.size();
+ */
}