public async Task SetAndGetTestCacheKeyEquality() { var store = new ImmutableInMemoryCacheStore(); var resp = new HttpResponseMessage(); var cacheKey = new CacheKey("key", null); await store.SetAsync(cacheKey, new CacheEntry(resp)); var fromCache = await store.GetAsync(new CacheKey("key", null)); // check Assert.AreEqual(resp, fromCache.HttpResponse); }
public async Task SetAndGetExisting() { var store = new ImmutableInMemoryCacheStore(); var resp = new HttpResponseMessage(); var cacheKey = new CacheKey("key", null); await store.SetAsync(cacheKey, new CacheEntry(resp)); var respNew = new HttpResponseMessage(); // overwrite await store.SetAsync(cacheKey, new CacheEntry(respNew)); var fromCache = await store.GetAsync(cacheKey); // check Assert.AreEqual(respNew, fromCache.HttpResponse); }
public async Task SetAndGetMultiple() { var store = new ImmutableInMemoryCacheStore(); var resp = new HttpResponseMessage(); var resp2 = new HttpResponseMessage(); var cacheKey = new CacheKey("key", null); var cacheKey2 = new CacheKey("key2", null); await store.SetAsync(cacheKey, new CacheEntry(resp)); await store.SetAsync(cacheKey2, new CacheEntry(resp2)); var fromCache = await store.GetAsync(cacheKey); var fromCache2 = await store.GetAsync(cacheKey2); // check Assert.AreEqual(resp, fromCache.HttpResponse); // check Assert.AreEqual(resp2, fromCache2.HttpResponse); }
public async Task GetNonExisting() { var store = new ImmutableInMemoryCacheStore(); var resp = new HttpResponseMessage(); var cacheKey = new CacheKey("key", null); await store.SetAsync(cacheKey, new CacheEntry(resp)); var fromCache = await store.GetAsync("key2"); // check Assert.AreEqual(default(CacheEntry), fromCache); }
public async Task GetNonExistingFromEmpty() { var store = new ImmutableInMemoryCacheStore(); var cacheKey = new CacheKey("key", null); var fromCache = await store.GetAsync(cacheKey); // check Assert.AreEqual(default(CacheEntry), fromCache); }
public Task SetAsync(CacheKey key, CacheEntry value) { do { var oldCache = _cache; IImmutableDictionary<CacheKey, CacheEntry> newCache; if (oldCache.ContainsKey(key)) { // overwrite. Dic is immutable: no lock needed. newCache = oldCache.SetItem(key, value); } else { // Add the value to cache dictionary. Dic is immutable: no lock needed. newCache = oldCache.Add(key, value); } // newCache = new cache dic, containing value. Check. // Interlocked.CompareExchange(ref _cache, newCache, oldCache): // // => if _cache is the same as oldcache, then replace // _cache by newCache. This is an effective check: if _cache is no longer the // same as oldCache, another thread has made a change to _cache. If that's the // case, we need to do the add again, as we'll want to make sure we always work // on the latest version - we don't want to loose changes to the cache. // // Call checks for reference equality, not an overridden Equals => we need // this reference check, new instance = different reference. // // CompareExchange always returns the value in "location", eg the first // parameter, BEFORE the exchange. So, if we check that value (_cache before // exchange) against the oldCache and if these are the same, add was succesful, // and thanks to the CompareExchange call, _cache is now set to newCache // compares oldCache with newCache - if these are now the s if (oldCache == Interlocked.CompareExchange(ref _cache, newCache, oldCache)) { // we can get out of the loop return Task.FromResult(true); } // CompareExchange failed => another thread has made a change to _cache. // We need to do the add again, as we'll want to make sure we always work // on the latest version - we don't want to loose changes to the cache. } while (true); }
public Task RemoveAsync(CacheKey key) { do { var oldCache = _cache; IImmutableDictionary<CacheKey, CacheEntry> newCache; if (oldCache.ContainsKey(key)) { // Remove. Dic is immutable: no lock needed. newCache = oldCache.Remove(key); } else { newCache = oldCache; } // compares oldCache with newCache - if these are now the s if (oldCache == Interlocked.CompareExchange(ref _cache, newCache, oldCache)) { // we can get out of the loop return Task.FromResult(true); } // CompareExchange failed => another thread has made a change to _cache. // We need to do the add again, as we'll want to make sure we always work // on the latest version - we don't want to loose changes to the cache. } while (true); }
public Task<CacheEntry> GetAsync(CacheKey key) { CacheEntry value; if (_cache.TryGetValue(key, out value)) { return Task.FromResult((CacheEntry)value); } else { return Task.FromResult(default(CacheEntry)); } }
private Task<HttpResponseMessage> HandleSendAndContinuationForPutPatch(CacheKey cacheKey, HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { return base.SendAsync(request, cancellationToken) .ContinueWith( task => { var serverResponse = task.Result; if (serverResponse.IsSuccessStatusCode) { // ensure no NULL dates if (serverResponse.Headers.Date == null) { serverResponse.Headers.Date = DateTimeOffset.UtcNow; } // should we clear? if ((_enableClearRelatedResourceRepresentationsAfterPut && request.Method == HttpMethod.Put) || (_enableClearRelatedResourceRepresentationsAfterPatch && request.Method.Method.ToLower() == "patch")) { // clear related resources // // - remove resource with cachekey. This must be done, as there's no // guarantee the new response is cacheable. // // - look for resources in cache that start with // the cachekey + "?" for querystring. _cacheStore.RemoveAsync(cacheKey); _cacheStore.RemoveRangeAsync(cacheKey.PrimaryKey + "?"); } // check the response: is this response allowed to be cached? bool isCacheable = HttpResponseHelpers.CanBeCached(serverResponse); if (isCacheable) { // add the response to cache _cacheStore.SetAsync(cacheKey, new CacheEntry(serverResponse)); } // what about vary by headers (=> key should take this into account)? } return serverResponse; }); }
private Task<HttpResponseMessage> HandleSendAndContinuation(CacheKey cacheKey, HttpRequestMessage request, System.Threading.CancellationToken cancellationToken, bool mustRevalidate) { return base.SendAsync(request, cancellationToken) .ContinueWith( task => { var serverResponse = task.Result; // if we had to revalidate & got a 304 returned, that means // we can get the response message from cache. if (mustRevalidate && serverResponse.StatusCode == HttpStatusCode.NotModified) { var cacheEntry = _cacheStore.GetAsync(cacheKey).Result; var responseFromCacheEntry = cacheEntry.HttpResponse; responseFromCacheEntry.RequestMessage = request; return responseFromCacheEntry; } if (serverResponse.IsSuccessStatusCode) { // ensure no NULL dates if (serverResponse.Headers.Date == null) { serverResponse.Headers.Date = DateTimeOffset.UtcNow; } // check the response: is this response allowed to be cached? bool isCacheable = HttpResponseHelpers.CanBeCached(serverResponse); if (isCacheable) { // add the response to cache _cacheStore.SetAsync(cacheKey, new CacheEntry(serverResponse)); } // what about vary by headers (=> key should take this into account)? } return serverResponse; }); }