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:
parent
194e4f5289
commit
c78032d638
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in New Issue