HTTPCLIENT-975: added CacheConfig settings to provide more detailed

control over the background revalidation thread pool, and enabled
the stale-while-revalidate feature by default with a single worker
thread and a max queue size of 100 pending revalidations, which
should be a pretty safe setting.


git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1050360 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Jonathan Moore 2010-12-17 12:15:50 +00:00
parent d012c1041c
commit 83efe306fd
6 changed files with 109 additions and 30 deletions

View File

@ -29,10 +29,12 @@ package org.apache.http.impl.client.cache;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@ -59,8 +61,14 @@ public class AsynchronousValidator {
* @param cachingClient * @param cachingClient
* @param numThreads * @param numThreads
*/ */
public AsynchronousValidator(CachingHttpClient cachingClient, int numThreads) { public AsynchronousValidator(CachingHttpClient cachingClient, CacheConfig config) {
this(cachingClient, Executors.newFixedThreadPool(numThreads)); this(cachingClient,
new ThreadPoolExecutor(config.getAsynchronousWorkersCore(),
config.getAsynchronousWorkersMax(),
(long)config.getAsynchronousWorkerIdleLifetimeSecs(),
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(config.getRevalidationQueueSize()))
);
} }
/** /**

View File

@ -51,22 +51,35 @@ public class CacheConfig {
*/ */
public final static boolean DEFAULT_HEURISTIC_CACHING_ENABLED = false; public final static boolean DEFAULT_HEURISTIC_CACHING_ENABLED = false;
/** Default coefficient used to heuristically determine freshness lifetime from /** Default coefficient used to heuristically determine freshness
* cache entry. * lifetime from the Last-Modified time of a cache entry.
*/ */
public final static float DEFAULT_HEURISTIC_COEFFICIENT = 0.1f; public final static float DEFAULT_HEURISTIC_COEFFICIENT = 0.1f;
/** Default lifetime in seconds to be assumed when we cannot calculate freshness /** Default lifetime in seconds to be assumed when we cannot calculate
* heuristically * freshness heuristically.
*/ */
public final static long DEFAULT_HEURISTIC_LIFETIME = 0; public final static long DEFAULT_HEURISTIC_LIFETIME = 0;
/** Default number of worker threads to allow for background revalidations /** Default number of worker threads to allow for background revalidations
* resulting from the stale-while-revalidate directive; 0 disables handling * resulting from the stale-while-revalidate directive.
* asynchronous revalidations.
*/ */
private static final int DEFAULT_STALE_WHILE_REVALIDATE_WORKERS = 0; private static final int DEFAULT_ASYNCHRONOUS_WORKERS_MAX = 1;
/** Default minimum number of worker threads to allow for background
* revalidations resulting from the stale-while-revalidate directive.
*/
private static final int DEFAULT_ASYNCHRONOUS_WORKERS_CORE = 1;
/** Default maximum idle lifetime for a background revalidation thread
* before it gets reclaimed.
*/
private static final int DEFAULT_ASYNCHRONOUS_WORKER_IDLE_LIFETIME_SECS = 60;
/** Default maximum queue length for background revalidation requests.
*/
private static final int DEFAULT_REVALIDATION_QUEUE_SIZE = 100;
private int maxObjectSizeBytes = DEFAULT_MAX_OBJECT_SIZE_BYTES; private int maxObjectSizeBytes = DEFAULT_MAX_OBJECT_SIZE_BYTES;
private int maxCacheEntries = DEFAULT_MAX_CACHE_ENTRIES; private int maxCacheEntries = DEFAULT_MAX_CACHE_ENTRIES;
private int maxUpdateRetries = DEFAULT_MAX_UPDATE_RETRIES; private int maxUpdateRetries = DEFAULT_MAX_UPDATE_RETRIES;
@ -74,7 +87,10 @@ public class CacheConfig {
private float heuristicCoefficient = DEFAULT_HEURISTIC_COEFFICIENT; private float heuristicCoefficient = DEFAULT_HEURISTIC_COEFFICIENT;
private long heuristicDefaultLifetime = DEFAULT_HEURISTIC_LIFETIME; private long heuristicDefaultLifetime = DEFAULT_HEURISTIC_LIFETIME;
private boolean isSharedCache = true; private boolean isSharedCache = true;
private int staleWhileRevalidateWorkers = DEFAULT_STALE_WHILE_REVALIDATE_WORKERS; private int asynchronousWorkersMax = DEFAULT_ASYNCHRONOUS_WORKERS_MAX;
private int asynchronousWorkersCore = DEFAULT_ASYNCHRONOUS_WORKERS_CORE;
private int asynchronousWorkerIdleLifetimeSecs = DEFAULT_ASYNCHRONOUS_WORKER_IDLE_LIFETIME_SECS;
private int revalidationQueueSize = DEFAULT_REVALIDATION_QUEUE_SIZE;
/** /**
* Returns the current maximum object size that will be cached. * Returns the current maximum object size that will be cached.
@ -182,20 +198,76 @@ public class CacheConfig {
} }
/** /**
* Set number of worker threads to allow for background revalidations resulting from, * Returns the maximum number of threads to allow for background
* the stale-while-revalidate directive, 0 disables handling of directive * revalidations due to the stale-while-revalidate directive. A
* @return * value of 0 means background revalidations are disabled.
*/ */
public int getStaleWhileRevalidateWorkers() { public int getAsynchronousWorkersMax() {
return staleWhileRevalidateWorkers; return asynchronousWorkersMax;
} }
/** /**
* Get number of worker threads to allow for background revalidations resulting from, * Sets the maximum number of threads to allow for background
* the stale-while-revalidate directive, 0 disables handling of directive * revalidations due to the stale-while-revalidate directive.
* @param max number of threads; a value of 0 disables background
* revalidations.
*/ */
public void setStaleWhileRevalidateWorkers(int staleWhileRevalidateWorkers) { public void setAsynchronousWorkersMax(int max) {
this.staleWhileRevalidateWorkers = staleWhileRevalidateWorkers; this.asynchronousWorkersMax = max;
} }
/**
* Returns the minimum number of threads to keep alive for background
* revalidations due to the stale-while-revalidate directive.
*/
public int getAsynchronousWorkersCore() {
return asynchronousWorkersCore;
}
/**
* Sets the minimum number of threads to keep alive for background
* revalidations due to the stale-while-revalidate directive.
* @param min should be greater than zero and less than or equal
* to <code>getAsynchronousWorkersMax()</code>
*/
public void setAsynchronousWorkersCore(int min) {
this.asynchronousWorkersCore = min;
}
/**
* Returns the current maximum idle lifetime in seconds for a
* background revalidation worker thread. If a worker thread is idle
* for this long, and there are more than the core number of worker
* threads alive, the worker will be reclaimed.
*/
public int getAsynchronousWorkerIdleLifetimeSecs() {
return asynchronousWorkerIdleLifetimeSecs;
}
/**
* Sets the current maximum idle lifetime in seconds for a
* background revalidation worker thread. If a worker thread is idle
* for this long, and there are more than the core number of worker
* threads alive, the worker will be reclaimed.
* @param secs idle lifetime in seconds
*/
public void setAsynchronousWorkerIdleLifetimeSecs(int secs) {
this.asynchronousWorkerIdleLifetimeSecs = secs;
}
/**
* Returns the current maximum queue size for background revalidations.
*/
public int getRevalidationQueueSize() {
return revalidationQueueSize;
}
/**
* Sets the current maximum queue size for background revalidations.
*/
public void setRevalidationQueueSize(int size) {
this.revalidationQueueSize = size;
}
} }

View File

@ -127,7 +127,7 @@ public class CachingHttpClient implements HttpClient {
this.responseCompliance = new ResponseProtocolCompliance(); this.responseCompliance = new ResponseProtocolCompliance();
this.requestCompliance = new RequestProtocolCompliance(); this.requestCompliance = new RequestProtocolCompliance();
this.asynchRevalidator = makeAsynchronousValidator(config.getStaleWhileRevalidateWorkers()); this.asynchRevalidator = makeAsynchronousValidator(config);
} }
public CachingHttpClient() { public CachingHttpClient() {
@ -197,13 +197,13 @@ public class CachingHttpClient implements HttpClient {
this.conditionalRequestBuilder = conditionalRequestBuilder; this.conditionalRequestBuilder = conditionalRequestBuilder;
this.responseCompliance = responseCompliance; this.responseCompliance = responseCompliance;
this.requestCompliance = requestCompliance; this.requestCompliance = requestCompliance;
this.asynchRevalidator = makeAsynchronousValidator(config.getStaleWhileRevalidateWorkers()); this.asynchRevalidator = makeAsynchronousValidator(config);
} }
private AsynchronousValidator makeAsynchronousValidator( private AsynchronousValidator makeAsynchronousValidator(
int numWorkers) { CacheConfig config) {
if (numWorkers > 0) { if (config.getAsynchronousWorkersMax() > 0) {
return new AsynchronousValidator(this, numWorkers); return new AsynchronousValidator(this, config);
} }
return null; return null;
} }

View File

@ -166,7 +166,10 @@ public class TestAsynchronousValidator {
@Test @Test
public void testRevalidateCacheEntryEndToEnd() throws ProtocolException, IOException, InterruptedException { public void testRevalidateCacheEntryEndToEnd() throws ProtocolException, IOException, InterruptedException {
impl = new AsynchronousValidator(mockClient, 1); CacheConfig config = new CacheConfig();
config.setAsynchronousWorkersMax(1);
config.setAsynchronousWorkersCore(1);
impl = new AsynchronousValidator(mockClient, config);
EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false); EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
EasyMock.expect(mockClient.revalidateCacheEntry(target, request, mockContext, mockCacheEntry)).andReturn(null); EasyMock.expect(mockClient.revalidateCacheEntry(target, request, mockContext, mockCacheEntry)).andReturn(null);

View File

@ -62,7 +62,6 @@ import org.easymock.Capture;
import org.easymock.classextension.EasyMock; import org.easymock.classextension.EasyMock;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
public class TestCachingHttpClient { public class TestCachingHttpClient {
@ -341,9 +340,6 @@ public class TestCachingHttpClient {
Assert.assertEquals(0, impl.getCacheUpdates()); Assert.assertEquals(0, impl.getCacheUpdates());
} }
// TODO: re-enable when background validation enabled by default, or adjust
// test to specify background validation in CacheConfig
@Ignore
@Test @Test
public void testUnsuitableValidatableCacheEntryCausesRevalidation() throws Exception { public void testUnsuitableValidatableCacheEntryCausesRevalidation() throws Exception {
mockImplMethods(REVALIDATE_CACHE_ENTRY); mockImplMethods(REVALIDATE_CACHE_ENTRY);

View File

@ -172,7 +172,7 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
public void testStaleWhileRevalidateReturnsStaleEntryWithWarning() public void testStaleWhileRevalidateReturnsStaleEntryWithWarning()
throws Exception { throws Exception {
params.setStaleWhileRevalidateWorkers(1); params.setAsynchronousWorkersMax(1);
impl = new CachingHttpClient(mockBackend, cache, params); impl = new CachingHttpClient(mockBackend, cache, params);
HttpRequest req1 = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1); HttpRequest req1 = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);