Checkin for caching the template matching for significant route finder performance improvements (#728)

This commit is contained in:
Phil Proctor 2018-12-26 15:05:20 -05:00 committed by Marcelo Castagna
parent ac211886f1
commit 9bbb6364f2
7 changed files with 124 additions and 114 deletions

View File

@ -28,7 +28,7 @@ namespace Ocelot.DownstreamRouteFinder.Finder
foreach (var reRoute in applicableReRoutes) foreach (var reRoute in applicableReRoutes)
{ {
var urlMatch = _urlMatcher.Match(upstreamUrlPath, upstreamQueryString, reRoute.UpstreamTemplatePattern.Template, reRoute.UpstreamTemplatePattern.ContainsQueryString); var urlMatch = _urlMatcher.Match(upstreamUrlPath, upstreamQueryString, reRoute.UpstreamTemplatePattern);
if (urlMatch.Data.Match) if (urlMatch.Data.Match)
{ {

View File

@ -1,9 +1,10 @@
using Ocelot.Responses; using Ocelot.Responses;
using Ocelot.Values;
namespace Ocelot.DownstreamRouteFinder.UrlMatcher namespace Ocelot.DownstreamRouteFinder.UrlMatcher
{ {
public interface IUrlPathToUrlTemplateMatcher public interface IUrlPathToUrlTemplateMatcher
{ {
Response<UrlMatch> Match(string upstreamUrlPath, string upstreamQueryString, string upstreamUrlPathTemplate, bool containsQueryString); Response<UrlMatch> Match(string upstreamUrlPath, string upstreamQueryString, UpstreamPathTemplate pathTemplate);
} }
} }

View File

@ -1,22 +1,21 @@
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Ocelot.Responses; using Ocelot.Responses;
using Ocelot.Values;
namespace Ocelot.DownstreamRouteFinder.UrlMatcher namespace Ocelot.DownstreamRouteFinder.UrlMatcher
{ {
public class RegExUrlMatcher : IUrlPathToUrlTemplateMatcher public class RegExUrlMatcher : IUrlPathToUrlTemplateMatcher
{ {
public Response<UrlMatch> Match(string upstreamUrlPath, string upstreamQueryString, string upstreamUrlPathTemplate, bool containsQueryString) public Response<UrlMatch> Match(string upstreamUrlPath, string upstreamQueryString, UpstreamPathTemplate pathTemplate)
{ {
var regex = new Regex(upstreamUrlPathTemplate); if (!pathTemplate.ContainsQueryString)
if (!containsQueryString)
{ {
return regex.IsMatch(upstreamUrlPath) return pathTemplate.Pattern.IsMatch(upstreamUrlPath)
? new OkResponse<UrlMatch>(new UrlMatch(true)) ? new OkResponse<UrlMatch>(new UrlMatch(true))
: new OkResponse<UrlMatch>(new UrlMatch(false)); : new OkResponse<UrlMatch>(new UrlMatch(false));
} }
return regex.IsMatch($"{upstreamUrlPath}{upstreamQueryString}") return pathTemplate.Pattern.IsMatch($"{upstreamUrlPath}{upstreamQueryString}")
? new OkResponse<UrlMatch>(new UrlMatch(true)) ? new OkResponse<UrlMatch>(new UrlMatch(true))
: new OkResponse<UrlMatch>(new UrlMatch(false)); : new OkResponse<UrlMatch>(new UrlMatch(false));
} }

View File

@ -1,3 +1,5 @@
using System.Text.RegularExpressions;
namespace Ocelot.Values namespace Ocelot.Values
{ {
public class UpstreamPathTemplate public class UpstreamPathTemplate
@ -8,6 +10,9 @@ namespace Ocelot.Values
Priority = priority; Priority = priority;
ContainsQueryString = containsQueryString; ContainsQueryString = containsQueryString;
OriginalValue = originalValue; OriginalValue = originalValue;
Pattern = template == null ?
new Regex("$^", RegexOptions.Compiled | RegexOptions.Singleline) :
new Regex(template, RegexOptions.Compiled | RegexOptions.Singleline);
} }
public string Template { get; } public string Template { get; }
@ -17,5 +22,7 @@ namespace Ocelot.Values
public bool ContainsQueryString { get; } public bool ContainsQueryString { get; }
public string OriginalValue { get; } public string OriginalValue { get; }
public Regex Pattern { get; }
} }
} }

View File

@ -5,6 +5,7 @@ using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Validators; using BenchmarkDotNet.Validators;
using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Values;
namespace Ocelot.Benchmarks namespace Ocelot.Benchmarks
{ {
@ -12,8 +13,8 @@ namespace Ocelot.Benchmarks
public class UrlPathToUrlPathTemplateMatcherBenchmarks : ManualConfig public class UrlPathToUrlPathTemplateMatcherBenchmarks : ManualConfig
{ {
private RegExUrlMatcher _urlPathMatcher; private RegExUrlMatcher _urlPathMatcher;
private UpstreamPathTemplate _pathTemplate;
private string _downstreamUrlPath; private string _downstreamUrlPath;
private string _downstreamUrlPathTemplate;
private string _upstreamQuery; private string _upstreamQuery;
public UrlPathToUrlPathTemplateMatcherBenchmarks() public UrlPathToUrlPathTemplateMatcherBenchmarks()
@ -27,14 +28,14 @@ namespace Ocelot.Benchmarks
public void SetUp() public void SetUp()
{ {
_urlPathMatcher = new RegExUrlMatcher(); _urlPathMatcher = new RegExUrlMatcher();
_pathTemplate = new UpstreamPathTemplate("api/product/products/{productId}/variants/", 0, false, null);
_downstreamUrlPath = "api/product/products/1/variants/?soldout=false"; _downstreamUrlPath = "api/product/products/1/variants/?soldout=false";
_downstreamUrlPathTemplate = "api/product/products/{productId}/variants/";
} }
[Benchmark(Baseline = true)] [Benchmark(Baseline = true)]
public void Baseline() public void Baseline()
{ {
_urlPathMatcher.Match(_downstreamUrlPath, _upstreamQuery, _downstreamUrlPathTemplate, false); _urlPathMatcher.Match(_downstreamUrlPath, _upstreamQuery, _pathTemplate);
} }
// * Summary * // * Summary *

View File

@ -624,7 +624,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true)))) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
.And(x => x.GivenTheUpstreamHttpMethodIs("Get")) .And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
.When(x => x.WhenICallTheFinder()) .When(x => x.WhenICallTheFinder())
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly(1)) .And(x => x.ThenTheUrlMatcherIsCalledCorrectly(1, 0))
.BDDfy(); .BDDfy();
} }
@ -677,7 +677,8 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "test")) .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "test"))
.Build() .Build()
))) )))
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly(2)) .And(x => x.ThenTheUrlMatcherIsCalledCorrectly(1, 0))
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly(1, 1))
.BDDfy(); .BDDfy();
} }
@ -706,32 +707,32 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
private void ThenTheUrlMatcherIsCalledCorrectly() private void ThenTheUrlMatcherIsCalledCorrectly()
{ {
_mockMatcher _mockMatcher
.Verify(x => x.Match(_upstreamUrlPath, _upstreamQuery, _reRoutesConfig[0].UpstreamTemplatePattern.Template, _reRoutesConfig[0].UpstreamTemplatePattern.ContainsQueryString), Times.Once); .Verify(x => x.Match(_upstreamUrlPath, _upstreamQuery, _reRoutesConfig[0].UpstreamTemplatePattern), Times.Once);
} }
private void ThenTheUrlMatcherIsCalledCorrectly(int times) private void ThenTheUrlMatcherIsCalledCorrectly(int times, int index = 0)
{ {
_mockMatcher _mockMatcher
.Verify(x => x.Match(_upstreamUrlPath, _upstreamQuery, _reRoutesConfig[0].UpstreamTemplatePattern.OriginalValue, _reRoutesConfig[0].UpstreamTemplatePattern.ContainsQueryString), Times.Exactly(times)); .Verify(x => x.Match(_upstreamUrlPath, _upstreamQuery, _reRoutesConfig[index].UpstreamTemplatePattern), Times.Exactly(times));
} }
private void ThenTheUrlMatcherIsCalledCorrectly(string expectedUpstreamUrlPath) private void ThenTheUrlMatcherIsCalledCorrectly(string expectedUpstreamUrlPath)
{ {
_mockMatcher _mockMatcher
.Verify(x => x.Match(expectedUpstreamUrlPath, _upstreamQuery, _reRoutesConfig[0].UpstreamTemplatePattern.OriginalValue, _reRoutesConfig[0].UpstreamTemplatePattern.ContainsQueryString), Times.Once); .Verify(x => x.Match(expectedUpstreamUrlPath, _upstreamQuery, _reRoutesConfig[0].UpstreamTemplatePattern), Times.Once);
} }
private void ThenTheUrlMatcherIsNotCalled() private void ThenTheUrlMatcherIsNotCalled()
{ {
_mockMatcher _mockMatcher
.Verify(x => x.Match(_upstreamUrlPath, _upstreamQuery, _reRoutesConfig[0].UpstreamTemplatePattern.OriginalValue, _reRoutesConfig[0].UpstreamTemplatePattern.ContainsQueryString), Times.Never); .Verify(x => x.Match(_upstreamUrlPath, _upstreamQuery, _reRoutesConfig[0].UpstreamTemplatePattern), Times.Never);
} }
private void GivenTheUrlMatcherReturns(Response<UrlMatch> match) private void GivenTheUrlMatcherReturns(Response<UrlMatch> match)
{ {
_match = match; _match = match;
_mockMatcher _mockMatcher
.Setup(x => x.Match(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>())) .Setup(x => x.Match(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<UpstreamPathTemplate>()))
.Returns(_match); .Returns(_match);
} }

View File

@ -1,5 +1,6 @@
using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Responses; using Ocelot.Responses;
using Ocelot.Values;
using Shouldly; using Shouldly;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
@ -270,7 +271,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
private void WhenIMatchThePaths() private void WhenIMatchThePaths()
{ {
_result = _urlMatcher.Match(_path, _queryString, _downstreamPathTemplate, _containsQueryString); _result = _urlMatcher.Match(_path, _queryString, new UpstreamPathTemplate(_downstreamPathTemplate, 0, _containsQueryString, _downstreamPathTemplate));
} }
private void ThenTheResultIsTrue() private void ThenTheResultIsTrue()