Skip to content

Commit 531e2ab

Browse files
authored
Optimized Query Planning (#6379)
1 parent 31be4a0 commit 531e2ab

File tree

82 files changed

+2339
-924
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+2339
-924
lines changed

src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ private Operation CreateOperation(
218218
while (Unsafe.IsAddressLessThan(ref variantsStart, ref variantsEnd))
219219
{
220220
variantsStart.Complete();
221-
variantsStart = ref Unsafe.Add(ref variantsStart, 1);
221+
variantsStart = ref Unsafe.Add(ref variantsStart, 1)!;
222222
}
223223

224224
#if NET5_0_OR_GREATER
@@ -229,7 +229,7 @@ private Operation CreateOperation(
229229
while (Unsafe.IsAddressLessThan(ref optStart, ref optEnd))
230230
{
231231
optStart.OptimizeOperation(context);
232-
optStart = ref Unsafe.Add(ref optStart, 1);
232+
optStart = ref Unsafe.Add(ref optStart, 1)!;
233233
}
234234
#else
235235
for (var i = 0; i < _operationOptimizers.Count; i++)
@@ -241,13 +241,13 @@ private Operation CreateOperation(
241241
CompleteResolvers(schema);
242242

243243
variantsSpan = variants.AsSpan();
244-
variantsStart = ref GetReference(variantsSpan);
245-
variantsEnd = ref Unsafe.Add(ref variantsStart, variantsSpan.Length);
244+
variantsStart = ref GetReference(variantsSpan)!;
245+
variantsEnd = ref Unsafe.Add(ref variantsStart, variantsSpan.Length)!;
246246

247247
while (Unsafe.IsAddressLessThan(ref variantsStart, ref variantsEnd))
248248
{
249249
variantsStart.Seal();
250-
variantsStart = ref Unsafe.Add(ref variantsStart, 1);
250+
variantsStart = ref Unsafe.Add(ref variantsStart, 1)!;
251251
}
252252
}
253253

src/HotChocolate/Fusion/src/Composition/FusionGraphComposer.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,4 +164,46 @@ public async ValueTask<Schema> ComposeAsync(
164164

165165
return log.HasErrors ? null : context.FusionGraph;
166166
}
167+
168+
/// <summary>
169+
/// Composes the subgraph schemas into a single,
170+
/// merged schema representing the fusion gateway configuration.
171+
/// </summary>
172+
/// <param name="configurations">
173+
/// The subgraph configurations to compose.
174+
/// </param>
175+
/// <param name="features">
176+
/// The composition feature flags.
177+
/// </param>
178+
/// <param name="fusionTypePrefix">
179+
/// The prefix that is used for the fusion types.
180+
/// </param>
181+
/// <param name="fusionTypeSelf">
182+
/// Defines if the fusion types should be prefixed with the subgraph name.
183+
/// </param>
184+
/// <param name="logFactory">
185+
/// A factory that creates a new composition log.
186+
/// </param>
187+
/// <param name="cancellationToken">
188+
/// A cancellation token that can be used to cancel the operation.
189+
/// </param>
190+
/// <returns>The fusion gateway configuration.</returns>
191+
public static async ValueTask<Schema> ComposeAsync(
192+
IEnumerable<SubgraphConfiguration> configurations,
193+
FusionFeatureCollection? features = null,
194+
string? fusionTypePrefix = null,
195+
bool fusionTypeSelf = false,
196+
Func<ICompositionLog>? logFactory = null,
197+
CancellationToken cancellationToken = default)
198+
{
199+
var composer = new FusionGraphComposer(
200+
fusionTypePrefix,
201+
fusionTypeSelf,
202+
logFactory);
203+
204+
return await composer.ComposeAsync(
205+
configurations,
206+
features,
207+
cancellationToken);
208+
}
167209
}

src/HotChocolate/Fusion/src/Core/DependencyInjection/FusionRequestExecutorBuilderExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
using HotChocolate.Execution.Configuration;
44
using HotChocolate.Execution.Pipeline;
55
using HotChocolate.Fusion.Clients;
6+
using HotChocolate.Fusion.Execution.Pipeline;
67
using HotChocolate.Fusion.Metadata;
7-
using HotChocolate.Fusion.Pipeline;
88
using HotChocolate.Fusion.Planning;
99
using HotChocolate.Language;
1010
using HotChocolate.Types.Descriptors;

src/HotChocolate/Fusion/src/Core/DependencyInjection/GatewayConfiguration.cs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,11 @@ namespace Microsoft.Extensions.DependencyInjection;
55
/// <summary>
66
/// Represents the Fusion gateway configuration.
77
/// </summary>
8-
public sealed class GatewayConfiguration
8+
public sealed class GatewayConfiguration(DocumentNode document)
99
{
10-
public GatewayConfiguration(DocumentNode document)
11-
{
12-
Document = document ?? throw new ArgumentNullException(nameof(document));
13-
}
14-
1510
/// <summary>
1611
/// Gets the Fusion gateway configuration document.
1712
/// </summary>
18-
public DocumentNode Document { get; }
13+
public DocumentNode Document { get; } = document
14+
?? throw new ArgumentNullException(nameof(document));
1915
}

src/HotChocolate/Fusion/src/Core/Execution/ExecutionState.cs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Diagnostics;
12
using HotChocolate.Execution.Processing;
23
using HotChocolate.Language;
34

@@ -7,17 +8,18 @@ namespace HotChocolate.Fusion.Execution;
78
/// Represents the query plan state for a single selection set.
89
/// This working state can be shared between nodes of a query plan.
910
/// </summary>
11+
[DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")]
1012
internal sealed class ExecutionState
1113
{
1214
public ExecutionState(
1315
SelectionSet selectionSet,
1416
ObjectResult selectionSetResult,
15-
IReadOnlyList<string> provides)
17+
IReadOnlyList<string> requires)
1618
{
1719
SelectionSet = selectionSet;
1820
SelectionSetResult = selectionSetResult;
1921
SelectionSetData = new SelectionData[selectionSet.Selections.Count];
20-
Provides = provides;
22+
Requires = requires;
2123
}
2224

2325
/// <summary>
@@ -28,10 +30,9 @@ public ExecutionState(
2830

2931
/// <summary>
3032
/// Gets a list of keys representing the state that is being
31-
/// provided after the associated <see cref="SelectionSet"/>
32-
/// has been executed.
33+
/// required to fetch data for the associated <see cref="SelectionSet"/>.
3334
/// </summary>
34-
public IReadOnlyList<string> Provides { get; }
35+
public IReadOnlyList<string> Requires { get; }
3536

3637
/// <summary>
3738
/// Gets the selection set that is being executed.
@@ -52,4 +53,16 @@ public ExecutionState(
5253
/// Gets a flag that indicates if the work item has been initialized.
5354
/// </summary>
5455
public bool IsInitialized { get; set; }
56+
57+
private string GetDebuggerDisplay()
58+
{
59+
var displayName = $"State {SelectionSet.Id}";
60+
61+
if (Requires.Count > 0)
62+
{
63+
displayName = $"{displayName} requires: {string.Join(", ", Requires)}";
64+
}
65+
66+
return displayName;
67+
}
5568
}

src/HotChocolate/Fusion/src/Core/Execution/ExecutorUtils.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -466,15 +466,15 @@ public static void TryInitializeExecutionState(QueryPlan queryPlan, ExecutionSta
466466
// next we will unwrap the results.
467467
ExtractSelectionResults(
468468
partialResult,
469-
(SelectionSet)executionState.SelectionSet,
469+
executionState.SelectionSet,
470470
executionState.SelectionSetData);
471471

472472
// last we will check if there are any exports for this selection-set.
473473
ExtractVariables(
474474
partialResult,
475475
queryPlan,
476476
executionState.SelectionSet,
477-
executionState.Provides,
477+
executionState.Requires,
478478
executionState.VariableValues);
479479
}
480480

src/HotChocolate/Fusion/src/Core/Execution/Nodes/If.cs

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,13 @@ namespace HotChocolate.Fusion.Execution.Nodes;
88
/// <summary>
99
/// The <see cref="If"/> node is responsible for executing a node based on a state.
1010
/// </summary>
11-
internal sealed class If : QueryPlanNode
11+
/// <param name="id">
12+
/// The unique id of this node.
13+
/// </param>
14+
internal sealed class If(int id) : QueryPlanNode(id)
1215
{
1316
private readonly List<Branch> _branches = new();
1417

15-
/// <summary>
16-
/// Initializes a new instance of <see cref="If"/>.
17-
/// </summary>
18-
/// <param name="id">
19-
/// The unique id of this node.
20-
/// </param>
21-
public If(int id) : base(id)
22-
{
23-
}
24-
2518
/// <summary>
2619
/// Gets the kind of this node.
2720
/// </summary>
@@ -65,15 +58,8 @@ public void AddBranch(string key, object value, QueryPlanNode node)
6558
throw ThrowHelper.Node_ReadOnly();
6659
}
6760

68-
if (key is null)
69-
{
70-
throw new ArgumentNullException(nameof(key));
71-
}
72-
73-
if (node is null)
74-
{
75-
throw new ArgumentNullException(nameof(node));
76-
}
61+
ArgumentNullException.ThrowIfNull(key);
62+
ArgumentNullException.ThrowIfNull(node);
7763

7864
_branches.Add(new Branch(key, value, node));
7965
base.AddNode(node);
@@ -103,6 +89,6 @@ protected override void FormatNodesProperty(Utf8JsonWriter writer)
10389
writer.WriteEndArray();
10490
}
10591
}
106-
92+
10793
private readonly record struct Branch(string Key, object Value, QueryPlanNode Node);
10894
}

src/HotChocolate/Fusion/src/Core/Execution/Nodes/Introspect.cs

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,19 @@ namespace HotChocolate.Fusion.Execution.Nodes;
1010
/// <summary>
1111
/// The <see cref="Introspect"/> node is responsible for executing introspection selection of the GraphQL request.
1212
/// </summary>
13-
internal sealed class Introspect : QueryPlanNode
13+
/// <param name="id">
14+
/// The unique id of this node.
15+
/// </param>
16+
/// <param name="selectionSet">
17+
/// The selection set for which the results shall be composed.
18+
/// </param>
19+
/// <exception cref="ArgumentNullException">
20+
/// <paramref name="selectionSet"/> is <c>null</c>.
21+
/// </exception>
22+
internal sealed class Introspect(int id, SelectionSet selectionSet) : QueryPlanNode(id)
1423
{
15-
private readonly SelectionSet _selectionSet;
16-
17-
/// <summary>
18-
/// Initializes a new instance of <see cref="Introspect"/>.
19-
/// </summary>
20-
/// <param name="id">
21-
/// The unique id of this node.
22-
/// </param>
23-
/// <param name="selectionSet">
24-
/// The selection set for which the results shall be composed.
25-
/// </param>
26-
/// <exception cref="ArgumentNullException">
27-
/// <paramref name="selectionSet"/> is <c>null</c>.
28-
/// </exception>
29-
public Introspect(int id, SelectionSet selectionSet) : base(id)
30-
{
31-
_selectionSet = selectionSet ?? throw new ArgumentNullException(nameof(selectionSet));
32-
}
24+
private readonly SelectionSet _selectionSet = selectionSet
25+
?? throw new ArgumentNullException(nameof(selectionSet));
3326

3427
/// <summary>
3528
/// Gets the kind of this node.
@@ -93,7 +86,7 @@ protected override void FormatProperties(Utf8JsonWriter writer)
9386
{
9487
rootSelectionNodes.Add(selection.SyntaxNode);
9588
}
96-
89+
9790
selection = ref Unsafe.Add(ref selection, 1)!;
9891
}
9992

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Diagnostics;
12
using System.Runtime.CompilerServices;
23
using System.Runtime.InteropServices;
34

@@ -6,16 +7,11 @@ namespace HotChocolate.Fusion.Execution.Nodes;
67
/// <summary>
78
/// The <see cref="Parallel"/> node executes its child nodes in parallel.
89
/// </summary>
9-
internal sealed class Parallel : QueryPlanNode
10+
/// <param name="id">
11+
/// The unique id of this node.
12+
/// </param>
13+
internal sealed class Parallel(int id) : QueryPlanNode(id)
1014
{
11-
/// <summary>
12-
/// Initializes a new instance of <see cref="Parallel"/>.
13-
/// </summary>
14-
/// <param name="id">
15-
/// The unique id of this node.
16-
/// </param>
17-
public Parallel(int id) : base(id) { }
18-
1915
/// <summary>
2016
/// Gets the kind of this node.
2117
/// </summary>
@@ -27,14 +23,26 @@ protected override async Task OnExecuteNodesAsync(
2723
CancellationToken cancellationToken)
2824
{
2925
InitializeNodes(context, cancellationToken, out var tasks);
26+
27+
#if DISABLED_FOR_DEBUGGING
28+
if(Debugger.IsAttached)
29+
{
30+
foreach (var task in tasks)
31+
{
32+
await task.ConfigureAwait(false);
33+
}
34+
return;
35+
}
36+
#endif
37+
3038
await Task.WhenAll(tasks).ConfigureAwait(false);
3139
}
3240

3341
private void InitializeNodes(FusionExecutionContext context, CancellationToken cancellationToken, out Task[] tasks)
3442
{
3543
var nodes = GetNodesSpan();
3644
tasks = new Task[nodes.Length];
37-
45+
3846
ref var node = ref MemoryMarshal.GetReference(nodes);
3947
ref var task = ref MemoryMarshal.GetArrayDataReference(tasks);
4048
ref var end = ref Unsafe.Add(ref node, nodes.Length);
@@ -46,4 +54,4 @@ private void InitializeNodes(FusionExecutionContext context, CancellationToken c
4654
task = ref Unsafe.Add(ref task, 1)!;
4755
}
4856
}
49-
}
57+
}

0 commit comments

Comments
 (0)