From 239dcfb6bd1734c1a18f6e26650b10109f9fc757 Mon Sep 17 00:00:00 2001 From: Tom Gardham-Pallister Date: Mon, 26 Jun 2017 19:10:20 +0100 Subject: [PATCH] working on region clearing cache, if using cachemanager back plance this would clear all servers in cluster --- src/Ocelot/Cache/IOcelotCache.cs | 6 +- .../Cache/Middleware/OutputCacheMiddleware.cs | 4 +- src/Ocelot/Cache/OcelotCacheManagerCache.cs | 18 +++-- .../ConsulOcelotConfigurationRepository.cs | 2 +- .../Controllers/OutputCacheController.cs | 33 +++++++++ .../Cache/CacheManagerCacheTests.cs | 32 ++++++++- .../Cache/OutputCacheMiddlewareTests.cs | 2 +- .../Controllers/OutputCacheControllerTests.cs | 70 +++++++++++++++++++ 8 files changed, 155 insertions(+), 12 deletions(-) create mode 100644 src/Ocelot/Controllers/OutputCacheController.cs create mode 100644 test/Ocelot.UnitTests/Controllers/OutputCacheControllerTests.cs diff --git a/src/Ocelot/Cache/IOcelotCache.cs b/src/Ocelot/Cache/IOcelotCache.cs index 9ac26d8f..9d7ead9b 100644 --- a/src/Ocelot/Cache/IOcelotCache.cs +++ b/src/Ocelot/Cache/IOcelotCache.cs @@ -1,11 +1,13 @@ using System; +using System.Collections.Generic; namespace Ocelot.Cache { public interface IOcelotCache { - void Add(string key, T value, TimeSpan ttl); - void AddAndDelete(string key, T value, TimeSpan ttl); + void Add(string key, T value, TimeSpan ttl, string region); + void AddAndDelete(string key, T value, TimeSpan ttl, string region); T Get(string key); + void ClearRegion(string region); } } diff --git a/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs b/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs index c2cb019e..fb1ebce5 100644 --- a/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs +++ b/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs @@ -63,7 +63,9 @@ namespace Ocelot.Cache.Middleware var response = HttpResponseMessage; - _outputCache.Add(downstreamUrlKey, response, TimeSpan.FromSeconds(DownstreamRoute.ReRoute.FileCacheOptions.TtlSeconds)); + var region = $"{DownstreamRoute.ReRoute.UpstreamHttpMethod}-{DownstreamRoute.ReRoute.UpstreamPathTemplate.Value}"; + + _outputCache.Add(downstreamUrlKey, response, TimeSpan.FromSeconds(DownstreamRoute.ReRoute.FileCacheOptions.TtlSeconds), region); _logger.LogDebug("finished response added to cache for {downstreamUrlKey}", downstreamUrlKey); } diff --git a/src/Ocelot/Cache/OcelotCacheManagerCache.cs b/src/Ocelot/Cache/OcelotCacheManagerCache.cs index e92d678d..4edfdaad 100644 --- a/src/Ocelot/Cache/OcelotCacheManagerCache.cs +++ b/src/Ocelot/Cache/OcelotCacheManagerCache.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using CacheManager.Core; namespace Ocelot.Cache @@ -6,18 +8,21 @@ namespace Ocelot.Cache public class OcelotCacheManagerCache : IOcelotCache { private readonly ICacheManager _cacheManager; + private HashSet _keys; public OcelotCacheManagerCache(ICacheManager cacheManager) { _cacheManager = cacheManager; + _keys = new HashSet(); } - public void Add(string key, T value, TimeSpan ttl) + public void Add(string key, T value, TimeSpan ttl, string region) { - _cacheManager.Add(new CacheItem(key, value, ExpirationMode.Absolute, ttl)); + _cacheManager.Add(new CacheItem(key, region, value, ExpirationMode.Absolute, ttl)); + _keys.Add(key); } - public void AddAndDelete(string key, T value, TimeSpan ttl) + public void AddAndDelete(string key, T value, TimeSpan ttl, string region) { var exists = _cacheManager.Get(key); @@ -26,12 +31,17 @@ namespace Ocelot.Cache _cacheManager.Remove(key); } - _cacheManager.Add(new CacheItem(key, value, ExpirationMode.Absolute, ttl)); + Add(key, value, ttl, region); } public T Get(string key) { return _cacheManager.Get(key); } + + public void ClearRegion(string region) + { + _cacheManager.ClearRegion(region); + } } } \ No newline at end of file diff --git a/src/Ocelot/Configuration/Repository/ConsulOcelotConfigurationRepository.cs b/src/Ocelot/Configuration/Repository/ConsulOcelotConfigurationRepository.cs index 703d039b..d7929aaa 100644 --- a/src/Ocelot/Configuration/Repository/ConsulOcelotConfigurationRepository.cs +++ b/src/Ocelot/Configuration/Repository/ConsulOcelotConfigurationRepository.cs @@ -68,7 +68,7 @@ namespace Ocelot.Configuration.Repository if (result.Response) { - _cache.AddAndDelete(_ocelotConfiguration, ocelotConfiguration, TimeSpan.FromSeconds(3)); + _cache.AddAndDelete(_ocelotConfiguration, ocelotConfiguration, TimeSpan.FromSeconds(3), "OcelotConfiguration"); return new OkResponse(); } diff --git a/src/Ocelot/Controllers/OutputCacheController.cs b/src/Ocelot/Controllers/OutputCacheController.cs new file mode 100644 index 00000000..2f56bf8d --- /dev/null +++ b/src/Ocelot/Controllers/OutputCacheController.cs @@ -0,0 +1,33 @@ +using System.Net.Http; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Ocelot.Cache; + +namespace Ocelot.Controllers +{ + [Authorize] + [Route("cache")] + public class OutputCacheController : Controller + { + private IOcelotCache _cache; + + public OutputCacheController(IOcelotCache cache) + { + _cache = cache; + } + + [HttpGet] + public IActionResult Get() + { + return new NotFoundResult(); + } + + [HttpDelete] + [Route("{region}")] + public IActionResult Delete(string region) + { + _cache.ClearRegion(region); + return new NoContentResult(); + } + } +} \ No newline at end of file diff --git a/test/Ocelot.UnitTests/Cache/CacheManagerCacheTests.cs b/test/Ocelot.UnitTests/Cache/CacheManagerCacheTests.cs index 87752a52..5780cff2 100644 --- a/test/Ocelot.UnitTests/Cache/CacheManagerCacheTests.cs +++ b/test/Ocelot.UnitTests/Cache/CacheManagerCacheTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using CacheManager.Core; using Moq; using Ocelot.Cache; @@ -16,12 +17,14 @@ namespace Ocelot.UnitTests.Cache private string _value; private string _resultGet; private TimeSpan _ttlSeconds; + private List _resultKeys; public CacheManagerCacheTests() { _mockCacheManager = new Mock>(); _ocelotOcelotCacheManager = new OcelotCacheManagerCache(_mockCacheManager.Object); } + [Fact] public void should_get_from_cache() { @@ -29,7 +32,6 @@ namespace Ocelot.UnitTests.Cache .When(x => x.WhenIGetFromTheCache()) .Then(x => x.ThenTheResultIs("someValue")) .BDDfy(); - } [Fact] @@ -40,13 +42,37 @@ namespace Ocelot.UnitTests.Cache .BDDfy(); } + [Fact] + public void should_delete_key_from_cache() + { + this.Given(_ => GivenTheFollowingRegion("fookey")) + .When(_ => WhenIDeleteTheRegion("fookey")) + .Then(_ => ThenTheRegionIsDeleted("fookey")) + .BDDfy(); + } + + private void WhenIDeleteTheRegion(string region) + { + _ocelotOcelotCacheManager.ClearRegion(region); + } + + private void ThenTheRegionIsDeleted(string region) + { + _mockCacheManager + .Verify(x => x.ClearRegion(region), Times.Once); + } + + private void GivenTheFollowingRegion(string key) + { + _ocelotOcelotCacheManager.Add(key, "doesnt matter", TimeSpan.FromSeconds(10), "region"); + } + private void WhenIAddToTheCache(string key, string value, TimeSpan ttlSeconds) { _key = key; _value = value; _ttlSeconds = ttlSeconds; - - _ocelotOcelotCacheManager.Add(_key, _value, _ttlSeconds); + _ocelotOcelotCacheManager.Add(_key, _value, _ttlSeconds, "region"); } private void ThenTheCacheIsCalledCorrectly() diff --git a/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs b/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs index dfc0220f..ab063b61 100644 --- a/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs @@ -124,7 +124,7 @@ namespace Ocelot.UnitTests.Cache private void ThenTheCacheAddIsCalledCorrectly() { _cacheManager - .Verify(x => x.Add(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + .Verify(x => x.Add(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } private void GivenResponseIsNotCached() diff --git a/test/Ocelot.UnitTests/Controllers/OutputCacheControllerTests.cs b/test/Ocelot.UnitTests/Controllers/OutputCacheControllerTests.cs new file mode 100644 index 00000000..5e2268af --- /dev/null +++ b/test/Ocelot.UnitTests/Controllers/OutputCacheControllerTests.cs @@ -0,0 +1,70 @@ +using Xunit; +using Shouldly; +using TestStack.BDDfy; +using Ocelot.Controllers; +using System; +using Moq; +using Ocelot.Cache; +using System.Net.Http; +using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc; + +namespace Ocelot.UnitTests.Controllers +{ + public class OutputCacheControllerTests + { + private OutputCacheController _controller; + private Mock> _cache; + private IActionResult _result; + + public OutputCacheControllerTests() + { + _cache = new Mock>(); + _controller = new OutputCacheController(_cache.Object); + } + + [Fact] + public void should_get_all_keys_from_server() + { + this.Given(_ => GivenTheFollowingKeys(new List{"b", "a"})) + .When(_ => WhenIGetTheKeys()) + .Then(_ => ThenTheKeysAreReturned()) + .BDDfy(); + } + + [Fact] + public void should_delete_key() + { + this.When(_ => WhenIDeleteTheKey("a")) + .Then(_ => ThenTheKeyIsDeleted("a")) + .BDDfy(); + } + + private void ThenTheKeyIsDeleted(string key) + { + _result.ShouldBeOfType(); + _cache + .Verify(x => x.ClearRegion(key), Times.Once); + } + + private void WhenIDeleteTheKey(string key) + { + _result = _controller.Delete(key); + } + + private void GivenTheFollowingKeys(List keys) + { + + } + + private void WhenIGetTheKeys() + { + _result = _controller.Get(); + } + + private void ThenTheKeysAreReturned() + { + _result.ShouldBeOfType(); + } + } +} \ No newline at end of file