Browse Source

Limit the number of parallel requests in GraphQL.

release/4.x
Sebastian 5 years ago
parent
commit
195d99aca7
  1. 88
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/QueryExecutionContext.cs
  2. 12
      backend/src/Squidex.Infrastructure/Assets/ImageSharp/ImageSharpAssetThumbnailGenerator.cs

88
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/QueryExecutionContext.cs

@ -9,6 +9,7 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Squidex.Domain.Apps.Entities.Assets; using Squidex.Domain.Apps.Entities.Assets;
using Squidex.Infrastructure; using Squidex.Infrastructure;
@ -17,6 +18,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
{ {
public class QueryExecutionContext : Dictionary<string, object> public class QueryExecutionContext : Dictionary<string, object>
{ {
private readonly SemaphoreSlim maxRequests = new SemaphoreSlim(10);
private readonly ConcurrentDictionary<Guid, IEnrichedContentEntity?> cachedContents = new ConcurrentDictionary<Guid, IEnrichedContentEntity?>(); private readonly ConcurrentDictionary<Guid, IEnrichedContentEntity?> cachedContents = new ConcurrentDictionary<Guid, IEnrichedContentEntity?>();
private readonly ConcurrentDictionary<Guid, IEnrichedAssetEntity?> cachedAssets = new ConcurrentDictionary<Guid, IEnrichedAssetEntity?>(); private readonly ConcurrentDictionary<Guid, IEnrichedAssetEntity?> cachedAssets = new ConcurrentDictionary<Guid, IEnrichedAssetEntity?>();
private readonly IContentQueryService contentQuery; private readonly IContentQueryService contentQuery;
@ -45,7 +47,15 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
if (asset == null) if (asset == null)
{ {
asset = await assetQuery.FindAssetAsync(context, id); await maxRequests.WaitAsync();
try
{
asset = await assetQuery.FindAssetAsync(context, id);
}
finally
{
maxRequests.Release();
}
if (asset != null) if (asset != null)
{ {
@ -62,7 +72,15 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
if (content == null) if (content == null)
{ {
content = await contentQuery.FindContentAsync(context, schemaId.ToString(), id); await maxRequests.WaitAsync();
try
{
content = await contentQuery.FindContentAsync(context, schemaId.ToString(), id);
}
finally
{
maxRequests.Release();
}
if (content != null) if (content != null)
{ {
@ -77,7 +95,17 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
{ {
var q = Q.Empty.WithODataQuery(odata); var q = Q.Empty.WithODataQuery(odata);
var assets = await assetQuery.QueryAsync(context, null, q); IResultList<IEnrichedAssetEntity> assets;
await maxRequests.WaitAsync();
try
{
assets = await assetQuery.QueryAsync(context, null, q);
}
finally
{
maxRequests.Release();
}
foreach (var asset in assets) foreach (var asset in assets)
{ {
@ -91,14 +119,24 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
{ {
var q = Q.Empty.WithODataQuery(odata); var q = Q.Empty.WithODataQuery(odata);
var result = await contentQuery.QueryAsync(context, schemaIdOrName, q); IResultList<IEnrichedContentEntity> contents;
await maxRequests.WaitAsync();
try
{
contents = await contentQuery.QueryAsync(context, schemaIdOrName, q);
}
finally
{
maxRequests.Release();
}
foreach (var content in result) foreach (var content in contents)
{ {
cachedContents[content.Id] = content; cachedContents[content.Id] = content;
} }
return result; return contents;
} }
public virtual async Task<IReadOnlyList<IEnrichedAssetEntity>> GetReferencedAssetsAsync(ICollection<Guid> ids) public virtual async Task<IReadOnlyList<IEnrichedAssetEntity>> GetReferencedAssetsAsync(ICollection<Guid> ids)
@ -109,7 +147,17 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
if (notLoadedAssets.Count > 0) if (notLoadedAssets.Count > 0)
{ {
var assets = await assetQuery.QueryAsync(context, null, Q.Empty.WithIds(notLoadedAssets)); IResultList<IEnrichedAssetEntity> assets;
await maxRequests.WaitAsync();
try
{
assets = await assetQuery.QueryAsync(context, null, Q.Empty.WithIds(notLoadedAssets));
}
finally
{
maxRequests.Release();
}
foreach (var asset in assets) foreach (var asset in assets)
{ {
@ -128,9 +176,19 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
if (notLoadedContents.Count > 0) if (notLoadedContents.Count > 0)
{ {
var result = await contentQuery.QueryAsync(context, notLoadedContents); IResultList<IEnrichedContentEntity> contents;
await maxRequests.WaitAsync();
try
{
contents = await contentQuery.QueryAsync(context, notLoadedContents);
}
finally
{
maxRequests.Release();
}
foreach (var content in result) foreach (var content in contents)
{ {
cachedContents[content.Id] = content; cachedContents[content.Id] = content;
} }
@ -139,11 +197,19 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
return ids.Select(cachedContents.GetOrDefault).NotNull().ToList(); return ids.Select(cachedContents.GetOrDefault).NotNull().ToList();
} }
public Task<IResultList<IEnrichedContentEntity>> QueryReferencingContentsAsync(string schemaIdOrName, string odata, Guid reference) public async Task<IResultList<IEnrichedContentEntity>> QueryReferencingContentsAsync(string schemaIdOrName, string odata, Guid reference)
{ {
var q = Q.Empty.WithODataQuery(odata).WithReference(reference); var q = Q.Empty.WithODataQuery(odata).WithReference(reference);
return contentQuery.QueryAsync(context, schemaIdOrName, q); await maxRequests.WaitAsync();
try
{
return await contentQuery.QueryAsync(context, schemaIdOrName, q);
}
finally
{
maxRequests.Release();
}
} }
} }
} }

12
backend/src/Squidex.Infrastructure/Assets/ImageSharp/ImageSharpAssetThumbnailGenerator.cs

@ -24,7 +24,7 @@ namespace Squidex.Infrastructure.Assets.ImageSharp
{ {
public sealed class ImageSharpAssetThumbnailGenerator : IAssetThumbnailGenerator public sealed class ImageSharpAssetThumbnailGenerator : IAssetThumbnailGenerator
{ {
private readonly SemaphoreSlim semaphoreSlim = new SemaphoreSlim(Math.Max(Environment.ProcessorCount / 4, 1)); private readonly SemaphoreSlim maxTasks = new SemaphoreSlim(Math.Max(Environment.ProcessorCount / 4, 1));
public async Task CreateThumbnailAsync(Stream source, Stream destination, ResizeOptions options) public async Task CreateThumbnailAsync(Stream source, Stream destination, ResizeOptions options)
{ {
@ -42,8 +42,7 @@ namespace Squidex.Infrastructure.Assets.ImageSharp
var w = options.Width ?? 0; var w = options.Width ?? 0;
var h = options.Height ?? 0; var h = options.Height ?? 0;
await semaphoreSlim.WaitAsync(); await maxTasks.WaitAsync();
try try
{ {
using (var image = Image.Load(source, out var format)) using (var image = Image.Load(source, out var format))
@ -89,7 +88,7 @@ namespace Squidex.Infrastructure.Assets.ImageSharp
} }
finally finally
{ {
semaphoreSlim.Release(); maxTasks.Release();
} }
} }
@ -156,8 +155,7 @@ namespace Squidex.Infrastructure.Assets.ImageSharp
Guard.NotNull(source, nameof(source)); Guard.NotNull(source, nameof(source));
Guard.NotNull(destination, nameof(destination)); Guard.NotNull(destination, nameof(destination));
await semaphoreSlim.WaitAsync(); await maxTasks.WaitAsync();
try try
{ {
using (var image = Image.Load(source, out var format)) using (var image = Image.Load(source, out var format))
@ -178,7 +176,7 @@ namespace Squidex.Infrastructure.Assets.ImageSharp
} }
finally finally
{ {
semaphoreSlim.Release(); maxTasks.Release();
} }
} }

Loading…
Cancel
Save