Skip to content

Commit

Permalink
Introduces fully serial execution strategy (#1552)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelstaib authored Mar 13, 2020
1 parent 47c61e1 commit 3b9e329
Show file tree
Hide file tree
Showing 21 changed files with 277 additions and 17 deletions.
93 changes: 93 additions & 0 deletions src/Core/Core.Tests/Execution/ExecutionStrategyResolverTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using HotChocolate.Execution.Configuration;
using HotChocolate.Language;
using Xunit;

namespace HotChocolate.Execution
{
public class ExecutionStrategyResolverTests
{
[Fact]
public void Default_Query_Strategy_Is_QueryExecutionStrategy()
{
// arrange
var options = new QueryExecutionOptions();
var strategyResolver = new ExecutionStrategyResolver(options, options);

// act
IExecutionStrategy strategy = strategyResolver.Resolve(OperationType.Query);

// assert
Assert.IsType<QueryExecutionStrategy>(strategy);
}

[Fact]
public void Default_Mutation_Strategy_Is_MutationStrategy()
{
// arrange
var options = new QueryExecutionOptions();
var strategyResolver = new ExecutionStrategyResolver(options, options);

// act
IExecutionStrategy strategy = strategyResolver.Resolve(OperationType.Mutation);

// assert
Assert.IsType<MutationExecutionStrategy>(strategy);
}

[Fact]
public void Default_Subscription_Strategy_Is_SubscriptionStrategy()
{
// arrange
var options = new QueryExecutionOptions();
var strategyResolver = new ExecutionStrategyResolver(options, options);

// act
IExecutionStrategy strategy = strategyResolver.Resolve(OperationType.Subscription);

// assert
Assert.IsType<SubscriptionExecutionStrategy>(strategy);
}

[Fact]
public void Serial_Query_Strategy_Is_QueryExecutionStrategy()
{
// arrange
var options = new QueryExecutionOptions { ForceSerialExecution = true };
var strategyResolver = new ExecutionStrategyResolver(options, options);

// act
IExecutionStrategy strategy = strategyResolver.Resolve(OperationType.Query);

// assert
Assert.IsType<SerialExecutionStrategy>(strategy);
}

[Fact]
public void Serial_Mutation_Strategy_Is_MutationStrategy()
{
// arrange
var options = new QueryExecutionOptions { ForceSerialExecution = true };
var strategyResolver = new ExecutionStrategyResolver(options, options);

// act
IExecutionStrategy strategy = strategyResolver.Resolve(OperationType.Mutation);

// assert
Assert.IsType<SerialExecutionStrategy>(strategy);
}

[Fact]
public void Serial_Subscription_Strategy_Is_SubscriptionStrategy()
{
// arrange
var options = new QueryExecutionOptions { ForceSerialExecution = true };
var strategyResolver = new ExecutionStrategyResolver(options, options);

// act
IExecutionStrategy strategy = strategyResolver.Resolve(OperationType.Subscription);

// assert
Assert.IsType<SubscriptionExecutionStrategy>(strategy);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public async Task ExecuteOperationMiddleware_Mutation_ExecutedSerially()
};

var options = new QueryExecutionOptions();
var strategyResolver = new ExecutionStrategyResolver(options);
var strategyResolver = new ExecutionStrategyResolver(options, options);

var diagnostics = new QueryExecutionDiagnostics(
new DiagnosticListener("Foo"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace HotChocolate.Execution.Configuration
{
/// <summary>
/// Represents a dedicated options accessor to read the execution strategy options
/// </summary>
public interface IExecutionStrategyOptionsAccessor
{
/// <summary>
/// Defines that the query graph shall be traversed and execution serially.
/// </summary>
bool? ForceSerialExecution { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ public interface IQueryExecutionOptionsAccessor
, IQueryCacheSizeOptionsAccessor
, IRequestTimeoutOptionsAccessor
, IValidateQueryOptionsAccessor
, IExecutionStrategyOptionsAccessor
{ }
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,5 +107,10 @@ public int QueryCacheSize
public TracingPreference TracingPreference { get; set; }

public bool? UseComplexityMultipliers { get; set; }

/// <summary>
/// Defines that the query graph shall be traversed and execution serially.
/// </summary>
public bool? ForceSerialExecution { get; set; }
}
}
21 changes: 14 additions & 7 deletions src/Core/Core/Execution/ExecutionStrategyResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,38 @@ namespace HotChocolate.Execution
internal class ExecutionStrategyResolver
: IExecutionStrategyResolver
{
private readonly Dictionary<OperationType, IExecutionStrategy> _strats;
private readonly Dictionary<OperationType, IExecutionStrategy> _strategies;

public ExecutionStrategyResolver(
IRequestTimeoutOptionsAccessor options)
IRequestTimeoutOptionsAccessor requestTimeoutOptions,
IExecutionStrategyOptionsAccessor strategyOptions)
{
_strats = new Dictionary<OperationType, IExecutionStrategy>()
bool serialExecution = strategyOptions.ForceSerialExecution ?? false;

_strategies = new Dictionary<OperationType, IExecutionStrategy>()
{
{
OperationType.Query,
new QueryExecutionStrategy()
serialExecution
? (IExecutionStrategy)new SerialExecutionStrategy()
: new QueryExecutionStrategy()
},
{
OperationType.Mutation,
new MutationExecutionStrategy()
serialExecution
? (IExecutionStrategy)new SerialExecutionStrategy()
: new MutationExecutionStrategy()
},
{
OperationType.Subscription,
new SubscriptionExecutionStrategy(options)
new SubscriptionExecutionStrategy(requestTimeoutOptions)
}
};
}

public IExecutionStrategy Resolve(OperationType operationType)
{
if (_strats.TryGetValue(operationType,
if (_strategies.TryGetValue(operationType,
out IExecutionStrategy strategy))
{
return strategy;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ public static IQueryExecutionBuilder UsePersistedQueryPipeline(
throw new ArgumentNullException(nameof(builder));
}

return builder
.UsePersistedQueryPipeline(new QueryExecutionOptions());
return builder.UsePersistedQueryPipeline(new QueryExecutionOptions());
}

public static IQueryExecutionBuilder UsePersistedQueryPipeline(
Expand Down Expand Up @@ -95,8 +94,7 @@ public static IQueryExecutionBuilder UseActivePersistedQueryPipeline(
throw new ArgumentNullException(nameof(builder));
}

return builder
.UseActivePersistedQueryPipeline(new QueryExecutionOptions());
return builder.UseActivePersistedQueryPipeline(new QueryExecutionOptions());
}

public static IQueryExecutionBuilder UseActivePersistedQueryPipeline(
Expand Down Expand Up @@ -168,6 +166,7 @@ public static IQueryExecutionBuilder UseInstrumentation(
builder
.RemoveService<DiagnosticListener>()
.RemoveService<DiagnosticSource>();

builder.Services
.AddSingleton(listener)
.AddSingleton<DiagnosticSource>(listener)
Expand Down Expand Up @@ -584,7 +583,8 @@ public static IQueryExecutionBuilder AddOptions(
.RemoveService<IInstrumentationOptionsAccessor>()
.RemoveService<IQueryCacheSizeOptionsAccessor>()
.RemoveService<IRequestTimeoutOptionsAccessor>()
.RemoveService<IValidateQueryOptionsAccessor>();
.RemoveService<IValidateQueryOptionsAccessor>()
.RemoveService<IExecutionStrategyOptionsAccessor>();
builder.Services.AddOptions(options);

return builder;
Expand All @@ -610,7 +610,8 @@ public static IServiceCollection AddOptions(
.AddSingleton<IInstrumentationOptionsAccessor>(options)
.AddSingleton<IQueryCacheSizeOptionsAccessor>(options)
.AddSingleton<IRequestTimeoutOptionsAccessor>(options)
.AddSingleton<IValidateQueryOptionsAccessor>(options);
.AddSingleton<IValidateQueryOptionsAccessor>(options)
.AddSingleton<IExecutionStrategyOptionsAccessor>(options);
}

public static IQueryExecutionBuilder AddParser(
Expand Down
128 changes: 128 additions & 0 deletions src/Core/Core/Execution/SerialExecutionStrategy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace HotChocolate.Execution
{
/// <summary>
/// This execution strategy executes the full query graph serailly.
/// This execution strategy is used to help with entity framework and
/// will be removed with version 11.
/// </summary>
internal sealed class SerialExecutionStrategy
: ExecutionStrategyBase
{
public override Task<IExecutionResult> ExecuteAsync(
IExecutionContext executionContext,
CancellationToken cancellationToken)
{
if (executionContext == null)
{
throw new ArgumentNullException(nameof(executionContext));
}

return ExecuteSeriallyAsync(executionContext, cancellationToken);
}

private static async Task<IExecutionResult> ExecuteSeriallyAsync(
IExecutionContext executionContext,
CancellationToken cancellationToken)
{
ResolverContext[] initialBatch =
CreateInitialBatch(executionContext,
executionContext.Result.Data);

BatchOperationHandler batchOperationHandler =
CreateBatchOperationHandler(executionContext);

try
{
await ExecuteResolverBatchSeriallyAsync(
executionContext,
initialBatch,
batchOperationHandler,
cancellationToken)
.ConfigureAwait(false);

EnsureRootValueNonNullState(
executionContext.Result,
initialBatch);

return executionContext.Result;
}
finally
{
batchOperationHandler?.Dispose();
ResolverContext.Return(initialBatch);
ArrayPool<ResolverContext>.Shared.Return(initialBatch);
}
}

private static async Task ExecuteResolverBatchSeriallyAsync(
IExecutionContext executionContext,
IEnumerable<ResolverContext> batch,
BatchOperationHandler batchOperationHandler,
CancellationToken cancellationToken)
{
var current = new List<ResolverContext>(batch);
var next = new List<ResolverContext>();

while (current.Count > 0)
{
foreach (ResolverContext resolverContext in current)
{
if (resolverContext is null)
{
break;
}

await ExecuteResolverSeriallyAsync(
resolverContext,
next.Add,
batchOperationHandler,
executionContext.ErrorHandler,
cancellationToken)
.ConfigureAwait(false);

cancellationToken.ThrowIfCancellationRequested();
}

ResolverContext.Return(current);

current.Clear();
current.AddRange(next);
next.Clear();

cancellationToken.ThrowIfCancellationRequested();
}
}

private static async Task ExecuteResolverSeriallyAsync(
ResolverContext resolverContext,
Action<ResolverContext> enqueueNext,
BatchOperationHandler batchOperationHandler,
IErrorHandler errorHandler,
CancellationToken cancellationToken)
{
resolverContext.Task = ExecuteResolverAsync(
resolverContext,
errorHandler);

if (batchOperationHandler != null)
{
await CompleteBatchOperationsAsync(
new[] { resolverContext },
batchOperationHandler,
cancellationToken)
.ConfigureAwait(false);
}

await resolverContext.Task.ConfigureAwait(false);

// serialize and integrate result into final query result
ValueCompletion.CompleteValue(enqueueNext, resolverContext);
}
}
}
6 changes: 3 additions & 3 deletions src/Core/Core/Execution/SubscriptionExecutionStrategy.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using HotChocolate.Execution.Configuration;
using HotChocolate.Properties;
using HotChocolate.Subscriptions;
using HotChocolate.Language;
using System.Collections.Immutable;
using HotChocolate.Properties;
using HotChocolate.Resolvers;
using HotChocolate.Subscriptions;

namespace HotChocolate.Execution
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"HotChocolate.AspNetCore.Subscriptions.Messages.IMessageHandler",
"HotChocolate.Execution.Batching.IBatchQueryExecutor",
"HotChocolate.Execution.Configuration.IErrorHandlerOptionsAccessor",
"HotChocolate.Execution.Configuration.IExecutionStrategyOptionsAccessor",
"HotChocolate.Execution.Configuration.IInstrumentationOptionsAccessor",
"HotChocolate.Execution.Configuration.IQueryCacheSizeOptionsAccessor",
"HotChocolate.Execution.Configuration.IQueryExecutionOptionsAccessor",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"HotChocolate.AspNetCore.Subscriptions.Messages.IMessageHandler",
"HotChocolate.Execution.Batching.IBatchQueryExecutor",
"HotChocolate.Execution.Configuration.IErrorHandlerOptionsAccessor",
"HotChocolate.Execution.Configuration.IExecutionStrategyOptionsAccessor",
"HotChocolate.Execution.Configuration.IInstrumentationOptionsAccessor",
"HotChocolate.Execution.Configuration.IQueryCacheSizeOptionsAccessor",
"HotChocolate.Execution.Configuration.IQueryExecutionOptionsAccessor",
Expand Down
Loading

0 comments on commit 3b9e329

Please sign in to comment.