HTTPCLIENT-1395: added config parameter to skip an extra cache entry freshness check upon cache update in case of a cache miss

This commit is contained in:
Oleg Kalnichevski 2018-01-01 18:31:23 +01:00
parent 194e4f5289
commit c78032d638
3 changed files with 107 additions and 74 deletions

View File

@ -486,72 +486,81 @@ public class AsyncCachingExec extends CachingExecBase implements AsyncExecChainH
} }
} }
@Override void triggerNewCacheEntryResponse(final HttpResponse backendResponse, final Date responseDate, final ByteArrayBuffer buffer) {
public void completed() {
final ComplexFuture<?> future = scope.future; final ComplexFuture<?> future = scope.future;
final CachingAsyncDataConsumer cachingDataConsumer = cachingConsumerRef.getAndSet(null); future.setDependency(responseCache.createCacheEntry(
if (cachingDataConsumer != null && !cachingDataConsumer.writtenThrough.get()) { target,
future.setDependency(responseCache.getCacheEntry(target, request, new FutureCallback<HttpCacheEntry>() { request,
backendResponse,
buffer,
requestDate,
responseDate,
new FutureCallback<HttpCacheEntry>() {
@Override @Override
public void completed(final HttpCacheEntry existingEntry) { public void completed(final HttpCacheEntry newEntry) {
final HttpResponse backendResponse = cachingDataConsumer.backendResponse; log.debug("Backend response successfully cached");
if (DateUtils.isAfter(existingEntry, backendResponse, HttpHeaders.DATE)) {
try { try {
final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, existingEntry); final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, newEntry);
triggerResponse(cacheResponse, scope, asyncExecCallback); triggerResponse(cacheResponse, scope, asyncExecCallback);
} catch (final ResourceIOException ex) { } catch (final ResourceIOException ex) {
asyncExecCallback.failed(ex); asyncExecCallback.failed(ex);
} }
} else {
final Date responseDate = cachingDataConsumer.responseDate;
final ByteArrayBuffer buffer = cachingDataConsumer.bufferRef.getAndSet(null);
future.setDependency(responseCache.createCacheEntry(
target,
request,
backendResponse,
buffer,
requestDate,
responseDate,
new FutureCallback<HttpCacheEntry>() {
@Override
public void completed(final HttpCacheEntry newEntry) {
log.debug("Backend response successfully cached");
try {
final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, newEntry);
triggerResponse(cacheResponse, scope, asyncExecCallback);
} catch (final ResourceIOException ex) {
asyncExecCallback.failed(ex);
}
}
@Override
public void failed(final Exception ex) {
asyncExecCallback.failed(ex);
}
@Override
public void cancelled() {
asyncExecCallback.failed(new InterruptedIOException());
}
}));
} }
}
@Override @Override
public void failed(final Exception cause) { public void failed(final Exception ex) {
asyncExecCallback.failed(cause); asyncExecCallback.failed(ex);
} }
@Override @Override
public void cancelled() { public void cancelled() {
asyncExecCallback.failed(new InterruptedIOException()); asyncExecCallback.failed(new InterruptedIOException());
} }
})); }));
}
@Override
public void completed() {
final CachingAsyncDataConsumer cachingDataConsumer = cachingConsumerRef.getAndSet(null);
if (cachingDataConsumer != null && !cachingDataConsumer.writtenThrough.get()) {
final ByteArrayBuffer buffer = cachingDataConsumer.bufferRef.getAndSet(null);
final HttpResponse backendResponse = cachingDataConsumer.backendResponse;
if (cacheConfig.isFreshnessCheckEnabled()) {
final ComplexFuture<?> future = scope.future;
future.setDependency(responseCache.getCacheEntry(target, request, new FutureCallback<HttpCacheEntry>() {
@Override
public void completed(final HttpCacheEntry existingEntry) {
if (DateUtils.isAfter(existingEntry, backendResponse, HttpHeaders.DATE)) {
log.debug("Backend already contains fresher cache entry");
try {
final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, existingEntry);
triggerResponse(cacheResponse, scope, asyncExecCallback);
} catch (final ResourceIOException ex) {
asyncExecCallback.failed(ex);
}
} else {
triggerNewCacheEntryResponse(backendResponse, responseDate, buffer);
}
}
@Override
public void failed(final Exception cause) {
asyncExecCallback.failed(cause);
}
@Override
public void cancelled() {
asyncExecCallback.failed(new InterruptedIOException());
}
}));
} else {
triggerNewCacheEntryResponse(backendResponse, responseDate, buffer);
}
} else { } else {
asyncExecCallback.completed(); asyncExecCallback.completed();
} }

View File

@ -168,7 +168,8 @@ public class CacheConfig implements Cloneable {
private final boolean heuristicCachingEnabled; private final boolean heuristicCachingEnabled;
private final float heuristicCoefficient; private final float heuristicCoefficient;
private final long heuristicDefaultLifetime; private final long heuristicDefaultLifetime;
private final boolean isSharedCache; private final boolean sharedCache;
private final boolean freshnessCheckEnabled;
private final int asynchronousWorkersMax; private final int asynchronousWorkersMax;
private final int asynchronousWorkersCore; private final int asynchronousWorkersCore;
private final int asynchronousWorkerIdleLifetimeSecs; private final int asynchronousWorkerIdleLifetimeSecs;
@ -184,7 +185,8 @@ public class CacheConfig implements Cloneable {
final boolean heuristicCachingEnabled, final boolean heuristicCachingEnabled,
final float heuristicCoefficient, final float heuristicCoefficient,
final long heuristicDefaultLifetime, final long heuristicDefaultLifetime,
final boolean isSharedCache, final boolean sharedCache,
final boolean freshnessCheckEnabled,
final int asynchronousWorkersMax, final int asynchronousWorkersMax,
final int asynchronousWorkersCore, final int asynchronousWorkersCore,
final int asynchronousWorkerIdleLifetimeSecs, final int asynchronousWorkerIdleLifetimeSecs,
@ -199,7 +201,8 @@ public class CacheConfig implements Cloneable {
this.heuristicCachingEnabled = heuristicCachingEnabled; this.heuristicCachingEnabled = heuristicCachingEnabled;
this.heuristicCoefficient = heuristicCoefficient; this.heuristicCoefficient = heuristicCoefficient;
this.heuristicDefaultLifetime = heuristicDefaultLifetime; this.heuristicDefaultLifetime = heuristicDefaultLifetime;
this.isSharedCache = isSharedCache; this.sharedCache = sharedCache;
this.freshnessCheckEnabled = freshnessCheckEnabled;
this.asynchronousWorkersMax = asynchronousWorkersMax; this.asynchronousWorkersMax = asynchronousWorkersMax;
this.asynchronousWorkersCore = asynchronousWorkersCore; this.asynchronousWorkersCore = asynchronousWorkersCore;
this.asynchronousWorkerIdleLifetimeSecs = asynchronousWorkerIdleLifetimeSecs; this.asynchronousWorkerIdleLifetimeSecs = asynchronousWorkerIdleLifetimeSecs;
@ -285,7 +288,17 @@ public class CacheConfig implements Cloneable {
* shared (private) cache * shared (private) cache
*/ */
public boolean isSharedCache() { public boolean isSharedCache() {
return isSharedCache; return sharedCache;
}
/**
* Returns whether the cache will perform an extra cache entry freshness check
* upon cache update in case of a cache miss
*
* @since 5.0
*/
public boolean isFreshnessCheckEnabled() {
return freshnessCheckEnabled;
} }
/** /**
@ -359,7 +372,8 @@ public class CacheConfig implements Cloneable {
private boolean heuristicCachingEnabled; private boolean heuristicCachingEnabled;
private float heuristicCoefficient; private float heuristicCoefficient;
private long heuristicDefaultLifetime; private long heuristicDefaultLifetime;
private boolean isSharedCache; private boolean sharedCache;
private boolean freshnessCheckEnabled;
private int asynchronousWorkersMax; private int asynchronousWorkersMax;
private int asynchronousWorkersCore; private int asynchronousWorkersCore;
private int asynchronousWorkerIdleLifetimeSecs; private int asynchronousWorkerIdleLifetimeSecs;
@ -372,10 +386,11 @@ public class CacheConfig implements Cloneable {
this.maxUpdateRetries = DEFAULT_MAX_UPDATE_RETRIES; this.maxUpdateRetries = DEFAULT_MAX_UPDATE_RETRIES;
this.allow303Caching = DEFAULT_303_CACHING_ENABLED; this.allow303Caching = DEFAULT_303_CACHING_ENABLED;
this.weakETagOnPutDeleteAllowed = DEFAULT_WEAK_ETAG_ON_PUTDELETE_ALLOWED; this.weakETagOnPutDeleteAllowed = DEFAULT_WEAK_ETAG_ON_PUTDELETE_ALLOWED;
this.heuristicCachingEnabled = false; this.heuristicCachingEnabled = DEFAULT_HEURISTIC_CACHING_ENABLED;
this.heuristicCoefficient = DEFAULT_HEURISTIC_COEFFICIENT; this.heuristicCoefficient = DEFAULT_HEURISTIC_COEFFICIENT;
this.heuristicDefaultLifetime = DEFAULT_HEURISTIC_LIFETIME; this.heuristicDefaultLifetime = DEFAULT_HEURISTIC_LIFETIME;
this.isSharedCache = true; this.sharedCache = true;
this.freshnessCheckEnabled = true;
this.asynchronousWorkersMax = DEFAULT_ASYNCHRONOUS_WORKERS_MAX; this.asynchronousWorkersMax = DEFAULT_ASYNCHRONOUS_WORKERS_MAX;
this.asynchronousWorkersCore = DEFAULT_ASYNCHRONOUS_WORKERS_CORE; this.asynchronousWorkersCore = DEFAULT_ASYNCHRONOUS_WORKERS_CORE;
this.asynchronousWorkerIdleLifetimeSecs = DEFAULT_ASYNCHRONOUS_WORKER_IDLE_LIFETIME_SECS; this.asynchronousWorkerIdleLifetimeSecs = DEFAULT_ASYNCHRONOUS_WORKER_IDLE_LIFETIME_SECS;
@ -468,12 +483,12 @@ public class CacheConfig implements Cloneable {
/** /**
* Sets whether the cache should behave as a shared cache or not. * Sets whether the cache should behave as a shared cache or not.
* @param isSharedCache true to behave as a shared cache, false to * @param sharedCache true to behave as a shared cache, false to
* behave as a non-shared (private) cache. To have the cache * behave as a non-shared (private) cache. To have the cache
* behave like a browser cache, you want to set this to {@code false}. * behave like a browser cache, you want to set this to {@code false}.
*/ */
public Builder setSharedCache(final boolean isSharedCache) { public Builder setSharedCache(final boolean sharedCache) {
this.isSharedCache = isSharedCache; this.sharedCache = sharedCache;
return this; return this;
} }
@ -542,7 +557,8 @@ public class CacheConfig implements Cloneable {
heuristicCachingEnabled, heuristicCachingEnabled,
heuristicCoefficient, heuristicCoefficient,
heuristicDefaultLifetime, heuristicDefaultLifetime,
isSharedCache, sharedCache,
freshnessCheckEnabled,
asynchronousWorkersMax, asynchronousWorkersMax,
asynchronousWorkersCore, asynchronousWorkersCore,
asynchronousWorkerIdleLifetimeSecs, asynchronousWorkerIdleLifetimeSecs,
@ -563,7 +579,8 @@ public class CacheConfig implements Cloneable {
.append(", heuristicCachingEnabled=").append(this.heuristicCachingEnabled) .append(", heuristicCachingEnabled=").append(this.heuristicCachingEnabled)
.append(", heuristicCoefficient=").append(this.heuristicCoefficient) .append(", heuristicCoefficient=").append(this.heuristicCoefficient)
.append(", heuristicDefaultLifetime=").append(this.heuristicDefaultLifetime) .append(", heuristicDefaultLifetime=").append(this.heuristicDefaultLifetime)
.append(", isSharedCache=").append(this.isSharedCache) .append(", sharedCache=").append(this.sharedCache)
.append(", freshnessCheckEnabled=").append(this.freshnessCheckEnabled)
.append(", asynchronousWorkersMax=").append(this.asynchronousWorkersMax) .append(", asynchronousWorkersMax=").append(this.asynchronousWorkersMax)
.append(", asynchronousWorkersCore=").append(this.asynchronousWorkersCore) .append(", asynchronousWorkersCore=").append(this.asynchronousWorkersCore)
.append(", asynchronousWorkerIdleLifetimeSecs=").append(this.asynchronousWorkerIdleLifetimeSecs) .append(", asynchronousWorkerIdleLifetimeSecs=").append(this.asynchronousWorkerIdleLifetimeSecs)

View File

@ -380,15 +380,22 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
buf = null; buf = null;
} }
backendResponse.close(); backendResponse.close();
final HttpCacheEntry existingEntry = responseCache.getCacheEntry(target, request);
if (DateUtils.isAfter(existingEntry, backendResponse, HttpHeaders.DATE)) { final HttpCacheEntry cacheEntry;
return convert(responseGenerator.generateResponse(request, existingEntry)); if (cacheConfig.isFreshnessCheckEnabled()) {
final HttpCacheEntry existingEntry = responseCache.getCacheEntry(target, request);
if (DateUtils.isAfter(existingEntry, backendResponse, HttpHeaders.DATE)) {
log.debug("Backend already contains fresher cache entry");
cacheEntry = existingEntry;
} else {
cacheEntry = responseCache.createCacheEntry(target, request, backendResponse, buf, requestSent, responseReceived);
log.debug("Backend response successfully cached");
}
} else { } else {
final HttpCacheEntry newEntry = responseCache.createCacheEntry( cacheEntry = responseCache.createCacheEntry(target, request, backendResponse, buf, requestSent, responseReceived);
target, request, backendResponse, buf, requestSent, responseReceived); log.debug("Backend response successfully cached (freshness check skipped)");
log.debug("Backend response successfully cached");
return convert(responseGenerator.generateResponse(request, newEntry));
} }
return convert(responseGenerator.generateResponse(request, cacheEntry));
} }
private ClassicHttpResponse handleCacheMiss( private ClassicHttpResponse handleCacheMiss(