Browse Source

Improvement for surrogate keys.

pull/493/head
Sebastian 6 years ago
parent
commit
b679d11156
  1. 2
      backend/src/Squidex.Web/Pipeline/ApiCostsFilter.cs
  2. 2
      backend/src/Squidex.Web/Pipeline/CachingFilter.cs
  3. 49
      backend/src/Squidex.Web/Pipeline/CachingManager.cs
  4. 2
      backend/src/Squidex.Web/Pipeline/CachingOptions.cs
  5. 4
      backend/src/Squidex/appsettings.json
  6. 23
      backend/tests/Squidex.Web.Tests/Pipeline/CachingFilterTests.cs

2
backend/src/Squidex.Web/Pipeline/ApiCostsFilter.cs

@ -70,6 +70,8 @@ namespace Squidex.Web.Pipeline
}
}
}
context.HttpContext.Response.Headers.Add("X-Costs", FilterDefinition.Costs.ToString());
}
await next();

2
backend/src/Squidex.Web/Pipeline/CachingFilter.cs

@ -68,7 +68,7 @@ namespace Squidex.Web.Pipeline
}
}
cachingManager.Finish(httpContext, cachingOptions.MaxSurrogateKeys);
cachingManager.Finish(httpContext, cachingOptions.MaxSurrogateKeysSize);
}
}
}

49
backend/src/Squidex.Web/Pipeline/CachingManager.cs

@ -12,6 +12,7 @@ using System.Security.Cryptography;
using System.Text;
using System.Threading;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using Squidex.Infrastructure;
@ -26,6 +27,9 @@ namespace Squidex.Web.Pipeline
internal sealed class CacheContext : IRequestCache, IDisposable
{
private static readonly ObjectPool<StringBuilder> StringBuilderPool =
new DefaultObjectPool<StringBuilder>(new StringBuilderPooledObjectPolicy());
private readonly IncrementalHash hasher = IncrementalHash.CreateHash(HashAlgorithmName.SHA256);
private readonly HashSet<string> keys = new HashSet<string>();
private readonly HashSet<string> headers = new HashSet<string>();
@ -83,7 +87,7 @@ namespace Squidex.Web.Pipeline
}
}
public void Finish(HttpResponse response, int maxSurrogateKeys)
public void Finish(HttpResponse response, int maxSurrogateKeySize)
{
if (hasDependency && !response.Headers.ContainsKey(HeaderNames.ETag))
{
@ -96,11 +100,44 @@ namespace Squidex.Web.Pipeline
}
}
if (keys.Count <= maxSurrogateKeys)
if (keys.Count > 0 && maxSurrogateKeySize > 0)
{
var value = string.Join(" ", keys);
const int GuidLength = 36;
var stringBuilder = StringBuilderPool.Get();
try
{
foreach (var key in keys)
{
if (stringBuilder.Length == 0)
{
if (stringBuilder.Length + GuidLength > maxSurrogateKeySize)
{
break;
}
}
else
{
if (stringBuilder.Length + GuidLength + 1 > maxSurrogateKeySize)
{
break;
}
stringBuilder.Append(' ');
}
stringBuilder.Append(key);
}
response.Headers.Add("Surrogate-Key", value);
if (stringBuilder.Length > 0)
{
response.Headers.Add("Surrogate-Key", stringBuilder.ToString());
}
}
finally
{
StringBuilderPool.Return(stringBuilder);
}
}
if (headers.Count > 0)
@ -180,7 +217,7 @@ namespace Squidex.Web.Pipeline
}
}
public void Finish(HttpContext httpContext, int maxSurrogateKeys)
public void Finish(HttpContext httpContext, int maxSurrogateKeySize)
{
Guard.NotNull(httpContext);
@ -188,7 +225,7 @@ namespace Squidex.Web.Pipeline
if (cacheContext != null)
{
cacheContext.Finish(httpContext.Response, maxSurrogateKeys);
cacheContext.Finish(httpContext.Response, maxSurrogateKeySize);
}
}
}

2
backend/src/Squidex.Web/Pipeline/CachingOptions.cs

@ -11,6 +11,6 @@ namespace Squidex.Web.Pipeline
{
public bool StrongETag { get; set; }
public int MaxSurrogateKeys { get; set; } = 200;
public int MaxSurrogateKeysSize { get; set; } = 17000;
}
}

4
backend/src/Squidex/appsettings.json

@ -37,9 +37,9 @@
"strongETag": false,
/*
* Restrict the surrogate keys to results that have less than 200 items.
* Restrict the surrogate keys to 17KB.
*/
"maxItemsForSurrogateKeys": 200
"maxSurrogateKeysSize": 17000
},
"languages": {

23
backend/tests/Squidex.Web.Tests/Pipeline/CachingFilterTests.cs

@ -215,7 +215,7 @@ namespace Squidex.Web.Pipeline
var id1 = Guid.NewGuid();
var id2 = Guid.NewGuid();
cachingOptions.MaxSurrogateKeys = 2;
cachingOptions.MaxSurrogateKeysSize = 100;
await sut.OnActionExecutionAsync(executingContext, () =>
{
@ -228,13 +228,32 @@ namespace Squidex.Web.Pipeline
Assert.Equal($"{id1} {id2}", httpContext.Response.Headers["Surrogate-Key"]);
}
[Fact]
public async Task Should_append_surrogate_keys_if_just_enough_space_for_one()
{
var id1 = Guid.NewGuid();
var id2 = Guid.NewGuid();
cachingOptions.MaxSurrogateKeysSize = 36;
await sut.OnActionExecutionAsync(executingContext, () =>
{
cachingManager.AddDependency(id1, 12);
cachingManager.AddDependency(id2, 12);
return Task.FromResult(executedContext);
});
Assert.Equal($"{id1}", httpContext.Response.Headers["Surrogate-Key"]);
}
[Fact]
public async Task Should_not_append_surrogate_keys_if_maximum_is_exceeded()
{
var id1 = Guid.NewGuid();
var id2 = Guid.NewGuid();
cachingOptions.MaxSurrogateKeys = 1;
cachingOptions.MaxSurrogateKeysSize = 20;
await sut.OnActionExecutionAsync(executingContext, () =>
{

Loading…
Cancel
Save