Browse Source

Improve timeout for query parsing.

pull/949/head
Sebastian 4 years ago
parent
commit
76e2716b73
  1. 14
      backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetQueryService.cs
  2. 23
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryParser.cs
  3. 16
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryService.cs
  4. 9
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/GeoQueryTransformer.cs

14
backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetQueryService.cs

@ -177,7 +177,7 @@ public sealed class AssetQueryService : IAssetQueryService
using (Telemetry.Activities.StartActivity("AssetQueryService/QueryAsync"))
{
q = await queryParser.ParseAsync(context, q, ct);
q = await ParseCoreAsync(context, q, ct);
var assets = await QueryCoreAsync(context, parentId, q, ct);
@ -215,6 +215,18 @@ public sealed class AssetQueryService : IAssetQueryService
}
}
private async Task<Q> ParseCoreAsync(Context context, Q q,
CancellationToken ct)
{
using (var combined = CancellationTokenSource.CreateLinkedTokenSource(ct))
{
// Enforce a hard timeout
combined.CancelAfter(options.TimeoutQuery);
return await queryParser.ParseAsync(context, q, ct);
}
}
private async Task<IResultList<IAssetFolderEntity>> QueryFoldersCoreAsync(Context context, DomainId parentId,
CancellationToken ct)
{

23
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryParser.cs

@ -30,7 +30,7 @@ public class ContentQueryParser
private static readonly TimeSpan CacheTime = TimeSpan.FromMinutes(60);
private readonly IMemoryCache cache;
private readonly IJsonSerializer serializer;
private readonly IAppProvider appprovider;
private readonly IAppProvider appProvider;
private readonly ITextIndex textIndex;
private readonly ContentOptions options;
@ -38,22 +38,23 @@ public class ContentQueryParser
IMemoryCache cache, IJsonSerializer serializer)
{
this.serializer = serializer;
this.appprovider = appprovider;
this.appProvider = appprovider;
this.textIndex = textIndex;
this.cache = cache;
this.options = options.Value;
}
public virtual async Task<Q> ParseAsync(Context context, Q q, ISchemaEntity? schema = null)
public virtual async Task<Q> ParseAsync(Context context, Q q, ISchemaEntity? schema = null,
CancellationToken ct = default)
{
Guard.NotNull(context);
Guard.NotNull(q);
using (Telemetry.Activities.StartActivity("ContentQueryParser/ParseAsync"))
{
var query = await ParseClrQueryAsync(context, q, schema);
var query = await ParseClrQueryAsync(context, q, schema, ct);
await TransformFilterAsync(query, context, schema);
await TransformFilterAsync(query, context, schema, ct);
WithSorting(query);
WithPaging(query, q);
@ -73,11 +74,12 @@ public class ContentQueryParser
}
}
private async Task TransformFilterAsync(ClrQuery query, Context context, ISchemaEntity? schema)
private async Task TransformFilterAsync(ClrQuery query, Context context, ISchemaEntity? schema,
CancellationToken ct)
{
if (query.Filter != null && schema != null)
{
query.Filter = await GeoQueryTransformer.TransformAsync(query.Filter, context, schema, textIndex);
query.Filter = await GeoQueryTransformer.TransformAsync(query.Filter, context, schema, textIndex, ct);
}
if (!string.IsNullOrWhiteSpace(query.FullText))
@ -93,7 +95,7 @@ public class ContentQueryParser
PreferredSchemaId = schema.Id
};
var fullTextIds = await textIndex.SearchAsync(context.App, textQuery, context.Scope());
var fullTextIds = await textIndex.SearchAsync(context.App, textQuery, context.Scope(), ct);
var fullTextFilter = ClrFilter.Eq("id", "__notfound__");
if (fullTextIds?.Any() == true)
@ -114,13 +116,14 @@ public class ContentQueryParser
}
}
private async Task<ClrQuery> ParseClrQueryAsync(Context context, Q q, ISchemaEntity? schema)
private async Task<ClrQuery> ParseClrQueryAsync(Context context, Q q, ISchemaEntity? schema,
CancellationToken ct)
{
var components = ResolvedComponents.Empty;
if (schema != null)
{
components = await appprovider.GetComponentsAsync(schema);
components = await appProvider.GetComponentsAsync(schema, ct);
}
var query = q.Query;

16
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryService.cs

@ -94,7 +94,7 @@ public sealed class ContentQueryService : IContentQueryService
q = q with { CreatedBy = context.UserPrincipal.Token() };
}
q = await queryParser.ParseAsync(context, q, schema);
q = await ParseCoreAsync(context, q, schema, ct);
var contents = await QueryCoreAsync(context, q, schema, ct);
@ -126,7 +126,7 @@ public sealed class ContentQueryService : IContentQueryService
return ResultList.Empty<IEnrichedContentEntity>();
}
q = await queryParser.ParseAsync(context, q);
q = await ParseCoreAsync(context, q, null, ct);
var contents = await QueryCoreAsync(context, q, schemas, ct);
@ -215,6 +215,18 @@ public sealed class ContentQueryService : IContentQueryService
return schemas.Where(x => IsAccessible(x) && HasPermission(context, x, PermissionIds.AppContentsReadOwn)).ToList();
}
private async Task<Q> ParseCoreAsync(Context context, Q q, ISchemaEntity? schema,
CancellationToken ct)
{
using (var combined = CancellationTokenSource.CreateLinkedTokenSource(ct))
{
// Enforce a hard timeout
combined.CancelAfter(options.TimeoutQuery);
return await queryParser.ParseAsync(context, q, schema, ct);
}
}
private async Task<IResultList<IContentEntity>> QueryCoreAsync(Context context, Q q, ISchemaEntity schema,
CancellationToken ct)
{

9
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/GeoQueryTransformer.cs

@ -17,15 +17,16 @@ internal sealed class GeoQueryTransformer : AsyncTransformVisitor<ClrValue, GeoQ
{
public static readonly GeoQueryTransformer Instance = new GeoQueryTransformer();
public record struct Args(Context Context, ISchemaEntity Schema, ITextIndex TextIndex);
public record struct Args(Context Context, ISchemaEntity Schema, ITextIndex TextIndex, CancellationToken CancellationToken);
private GeoQueryTransformer()
{
}
public static async Task<FilterNode<ClrValue>?> TransformAsync(FilterNode<ClrValue> filter, Context context, ISchemaEntity schema, ITextIndex textIndex)
public static async Task<FilterNode<ClrValue>?> TransformAsync(FilterNode<ClrValue> filter, Context context, ISchemaEntity schema, ITextIndex textIndex,
CancellationToken ct)
{
var args = new Args(context, schema, textIndex);
var args = new Args(context, schema, textIndex, ct);
return await filter.Accept(Instance, args);
}
@ -39,7 +40,7 @@ internal sealed class GeoQueryTransformer : AsyncTransformVisitor<ClrValue, GeoQ
var searchQuery = new GeoQuery(args.Schema.Id, field, sphere.Latitude, sphere.Longitude, sphere.Radius, 1000);
var searchScope = args.Context.Scope();
var ids = await args.TextIndex.SearchAsync(args.Context.App, searchQuery, searchScope);
var ids = await args.TextIndex.SearchAsync(args.Context.App, searchQuery, searchScope, args.CancellationToken);
if (ids == null || ids.Count == 0)
{

Loading…
Cancel
Save