From a55c75efdc563a849a3d65ca0768096b0d8b9c26 Mon Sep 17 00:00:00 2001 From: Tom Pallister Date: Tue, 15 May 2018 20:39:15 +0100 Subject: [PATCH] decided to stick a basic cache in for downstream route creator, can make fancy if required (#359) --- .../Finder/DownstreamRouteCreator.cs | 27 ++++++- .../DownstreamRouteCreatorTests.cs | 74 +++++++++++++++++++ 2 files changed, 99 insertions(+), 2 deletions(-) diff --git a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteCreator.cs b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteCreator.cs index f3a63f543..71940d18d 100644 --- a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteCreator.cs +++ b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteCreator.cs @@ -1,5 +1,6 @@ namespace Ocelot.DownstreamRouteFinder.Finder { + using System.Collections.Concurrent; using System.Collections.Generic; using Configuration; using Configuration.Builder; @@ -12,14 +13,16 @@ public class DownstreamRouteCreator : IDownstreamRouteProvider { private readonly IQoSOptionsCreator _qoSOptionsCreator; + private readonly ConcurrentDictionary> _cache; public DownstreamRouteCreator(IQoSOptionsCreator qoSOptionsCreator) { _qoSOptionsCreator = qoSOptionsCreator; + _cache = new ConcurrentDictionary>(); } public Response Get(string upstreamUrlPath, string upstreamHttpMethod, IInternalConfiguration configuration, string upstreamHost) - { + { var serviceName = GetServiceName(upstreamUrlPath); var downstreamPath = GetDownstreamPath(upstreamUrlPath); @@ -33,6 +36,11 @@ public Response Get(string upstreamUrlPath, string upstreamHttp var loadBalancerKey = CreateLoadBalancerKey(downstreamPathForKeys, upstreamHttpMethod, configuration.LoadBalancerOptions); + if(_cache.TryGetValue(loadBalancerKey, out var downstreamRoute)) + { + return downstreamRoute; + } + var qosOptions = _qoSOptionsCreator.Create(configuration.QoSOptions, downstreamPathForKeys, new []{ upstreamHttpMethod }); var downstreamReRoute = new DownstreamReRouteBuilder() @@ -51,7 +59,11 @@ public Response Get(string upstreamUrlPath, string upstreamHttp .WithUpstreamHttpMethod(new List(){ upstreamHttpMethod }) .Build(); - return new OkResponse(new DownstreamRoute(new List(), reRoute)); + downstreamRoute = new OkResponse(new DownstreamRoute(new List(), reRoute)); + + _cache.AddOrUpdate(loadBalancerKey, downstreamRoute, (x, y) => downstreamRoute); + + return downstreamRoute; } private static string RemoveQueryString(string downstreamPath) @@ -67,12 +79,23 @@ private static bool HasQueryString(string downstreamPath) private static string GetDownstreamPath(string upstreamUrlPath) { + if(upstreamUrlPath.IndexOf('/', 1) == -1) + { + return "/"; + } + return upstreamUrlPath .Substring(upstreamUrlPath.IndexOf('/', 1)); } private static string GetServiceName(string upstreamUrlPath) { + if(upstreamUrlPath.IndexOf('/', 1) == -1) + { + return upstreamUrlPath + .Substring(1); + } + return upstreamUrlPath .Substring(1, upstreamUrlPath.IndexOf('/', 1)) .TrimEnd('/'); diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteCreatorTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteCreatorTests.cs index e0ffa10f6..b4f827035 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteCreatorTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteCreatorTests.cs @@ -6,6 +6,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder { + using System; using Moq; using Ocelot.Configuration.Builder; using Ocelot.Configuration.Creator; @@ -26,6 +27,7 @@ public class DownstreamRouteCreatorTests private string _upstreamHttpMethod; private IInternalConfiguration _configuration; private Mock _qosOptionsCreator; + private Response _resultTwo; public DownstreamRouteCreatorTests() { @@ -50,6 +52,32 @@ public void should_create_downstream_route() .BDDfy(); } + [Fact] + public void should_cache_downstream_route() + { + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions); + + this.Given(_ => GivenTheConfiguration(configuration, "/geoffisthebest/")) + .When(_ => WhenICreate()) + .And(_ => GivenTheConfiguration(configuration, "/geoffisthebest/")) + .When(_ => WhenICreateAgain()) + .Then(_ => ThenTheDownstreamRoutesAreTheSameReference()) + .BDDfy(); + } + + [Fact] + public void should_not_cache_downstream_route() + { + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions); + + this.Given(_ => GivenTheConfiguration(configuration, "/geoffistheworst/")) + .When(_ => WhenICreate()) + .And(_ => GivenTheConfiguration(configuration, "/geoffisthebest/")) + .When(_ => WhenICreateAgain()) + .Then(_ => ThenTheDownstreamRoutesAreTheNotSameReference()) + .BDDfy(); + } + [Fact] public void should_create_downstream_route_with_no_path() { @@ -62,6 +90,30 @@ public void should_create_downstream_route_with_no_path() .BDDfy(); } + [Fact] + public void should_create_downstream_route_with_only_first_segment_no_traling_slash() + { + var upstreamUrlPath = "/auth"; + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions); + + this.Given(_ => GivenTheConfiguration(configuration, upstreamUrlPath)) + .When(_ => WhenICreate()) + .Then(_ => ThenTheDownstreamPathIsForwardSlash()) + .BDDfy(); + } + + [Fact] + public void should_create_downstream_route_with_segments_no_traling_slash() + { + var upstreamUrlPath = "/auth/test"; + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions); + + this.Given(_ => GivenTheConfiguration(configuration, upstreamUrlPath)) + .When(_ => WhenICreate()) + .Then(_ => ThenThePathDoesNotHaveTrailingSlash()) + .BDDfy(); + } + [Fact] public void should_create_downstream_route_and_remove_query_string() { @@ -143,6 +195,13 @@ private void ThenTheDownstreamPathIsForwardSlash() _result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerKey.ShouldBe("/auth/|GET"); } + private void ThenThePathDoesNotHaveTrailingSlash() + { + _result.Data.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe("/test"); + _result.Data.ReRoute.DownstreamReRoute[0].ServiceName.ShouldBe("auth"); + _result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerKey.ShouldBe("/auth/test|GET"); + } + private void ThenTheQueryStringIsRemoved() { _result.Data.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe("/test"); @@ -190,5 +249,20 @@ private void WhenICreate() { _result = _creator.Get(_upstreamUrlPath, _upstreamHttpMethod, _configuration, _upstreamHost); } + + private void WhenICreateAgain() + { + _resultTwo = _creator.Get(_upstreamUrlPath, _upstreamHttpMethod, _configuration, _upstreamHost); + } + + private void ThenTheDownstreamRoutesAreTheSameReference() + { + _result.ShouldBe(_resultTwo); + } + + private void ThenTheDownstreamRoutesAreTheNotSameReference() + { + _result.ShouldNotBe(_resultTwo); + } } }