Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public bool TryGetTypeScatter(
out TypeScatterInfo scatter)
=> _typeScatter.TryGetValue(typeName, out scatter);

private static HashSet<string> CollectSchemaNames(
private static ImmutableArray<string> CollectSchemaNames(
IEnumerable<FusionComplexTypeDefinition> complexTypes)
{
var schemaNames = new HashSet<string>(StringComparer.Ordinal);
Expand All @@ -94,7 +94,7 @@ private static HashSet<string> CollectSchemaNames(
}
}

return schemaNames;
return [.. schemaNames.OrderBy(static t => t, StringComparer.Ordinal)];
}

private static Dictionary<FieldKey, FieldResolutionInfo> BuildFieldResolutions(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ private static void IndexDependencies(
// Plan steps store which steps they feed into ("dependents").
// We invert that here so each step knows which steps it
// depends on, which is what the executor needs for scheduling.
foreach (var dependent in operationPlanStep.Dependents)
foreach (var dependent in operationPlanStep.Dependents.Order())
{
if (!ctx.DependenciesByStepId.TryGetValue(dependent, out var dependencies))
{
Expand Down Expand Up @@ -446,7 +446,7 @@ private static void IndexDependencies(

ctx.BranchesByNodeId.Add(
nodePlanStep.Id,
nodePlanStep.Branches.ToDictionary(x => x.Key, x => x.Value.Id));
nodePlanStep.Branches.ToDictionary(static t => t.Key, static t => t.Value.Id));
ctx.FallbackByNodeId.Add(nodePlanStep.Id, nodePlanStep.FallbackQuery.Id);
}
}
Expand Down Expand Up @@ -566,7 +566,7 @@ private static OperationExecutionNode CreateOperationExecutionNode(
operationStep.Conditions,
requiresFileUpload);

foreach (var parentDependency in operationStep.ParentDependencies)
foreach (var parentDependency in operationStep.ParentDependencies.OrderBy(static t => t.StepId))
{
node.AddParentDependency(parentDependency.StepId);
}
Expand Down Expand Up @@ -615,9 +615,9 @@ private static void MergeAndBatchOperations(
// step below will rewrite the dependency lookup as it merges nodes.
var originalDependencies = new Dictionary<int, int[]>(ctx.DependenciesByStepId.Count);

foreach (var (nodeId, dependencies) in ctx.DependenciesByStepId)
foreach (var (nodeId, dependencies) in ctx.DependenciesByStepId.OrderBy(static t => t.Key))
{
originalDependencies[nodeId] = dependencies.ToArray();
originalDependencies[nodeId] = [.. dependencies.Order()];
}

var perOperationDependencies = GroupBySchemaAndDepthIntoBatches(
Expand All @@ -639,7 +639,7 @@ private static Dictionary<int, MergeResult> MergeStructurallyIdenticalOperations
{
var candidates = new Dictionary<string, List<OperationExecutionNode>>(StringComparer.Ordinal);

foreach (var node in ctx.ExecutionNodes.Values.OfType<OperationExecutionNode>())
foreach (var node in ctx.ExecutionNodes.Values.OfType<OperationExecutionNode>().OrderBy(static t => t.Id))
{
if (node.Operation.Type != OperationType.Query)
{
Expand All @@ -664,7 +664,7 @@ private static Dictionary<int, MergeResult> MergeStructurallyIdenticalOperations

var mergeResults = new Dictionary<int, MergeResult>();

foreach (var (_, equivalentNodes) in candidates)
foreach (var (_, equivalentNodes) in candidates.OrderBy(static t => t.Key, StringComparer.Ordinal))
{
if (equivalentNodes.Count <= 1)
{
Expand Down Expand Up @@ -770,8 +770,8 @@ private static Dictionary<OperationBatchExecutionNode, Dictionary<int, int[]>>

var queryNodes = ctx.ExecutionNodes.Values
.OfType<OperationExecutionNode>()
.Where(n => n.Operation.Type == OperationType.Query)
.Where(n => !IsNodeFieldBound(n.Id, ctx, nodeFieldBoundCache))
.Where(n => n.Operation.Type == OperationType.Query && !IsNodeFieldBound(n.Id, ctx, nodeFieldBoundCache))
.OrderBy(static t => t.Id)
.ToList();

var depthLookup = new Dictionary<int, int>();
Expand Down Expand Up @@ -801,7 +801,9 @@ private static Dictionary<OperationBatchExecutionNode, Dictionary<int, int[]>>

// Process from shallowest to deepest so that deeper groups
// reference the already-redirected identifiers from earlier merges.
foreach (var (_, groupMembers) in batchGroups.OrderBy(t => t.Key.depth))
foreach (var (_, groupMembers) in batchGroups
.OrderBy(static t => t.Key.depth)
.ThenBy(static t => t.Key.schema, StringComparer.Ordinal))
{
if (groupMembers.Count <= 1)
{
Expand Down Expand Up @@ -864,7 +866,7 @@ private static void WrapRemainingMergedOperations(
Dictionary<OperationBatchExecutionNode, Dictionary<int, int[]>> perOperationDependencies,
Dictionary<int, int[]> originalDependencies)
{
foreach (var (primaryId, merge) in remainingMerges)
foreach (var (primaryId, merge) in remainingMerges.OrderBy(static t => t.Key))
{
var operationDefinition = CreateBatchOperationDefinition(merge);
var standaloneBatchNode = new OperationBatchExecutionNode(primaryId, [operationDefinition]);
Expand Down Expand Up @@ -896,7 +898,7 @@ private static void WirePerOperationDependencies(

var planNodeById = new Dictionary<int, IOperationPlanNode>();

foreach (var node in ctx.ExecutionNodes.Values)
foreach (var node in ctx.ExecutionNodes.Values.OrderBy(static t => t.Id))
{
planNodeById[node.Id] = node;

Expand All @@ -909,9 +911,9 @@ private static void WirePerOperationDependencies(
}
}

foreach (var (_, memberDependencies) in perOperationDependencies)
foreach (var (_, memberDependencies) in perOperationDependencies.OrderBy(static t => t.Key.Id))
{
foreach (var (operationId, dependencyIds) in memberDependencies)
foreach (var (operationId, dependencyIds) in memberDependencies.OrderBy(static t => t.Key))
{
if (planNodeById.TryGetValue(operationId, out var operationNode)
&& operationNode is OperationDefinition operationDefinition)
Expand Down Expand Up @@ -1110,7 +1112,7 @@ private static void WireOperationDependencies(ExecutionPlanBuildContext ctx)
// inner operation identifier also maps back to the parent batch node.
var executionNodeById = new Dictionary<int, ExecutionNode>();

foreach (var node in ctx.ExecutionNodes.Values)
foreach (var node in ctx.ExecutionNodes.Values.OrderBy(static t => t.Id))
{
executionNodeById[node.Id] = node;

Expand All @@ -1123,7 +1125,7 @@ private static void WireOperationDependencies(ExecutionPlanBuildContext ctx)
}
}

foreach (var (nodeId, stepDependencies) in ctx.DependenciesByStepId)
foreach (var (nodeId, stepDependencies) in ctx.DependenciesByStepId.OrderBy(static t => t.Key))
{
if (!ctx.ExecutionNodes.TryGetValue(nodeId, out var entry)
|| entry is not (OperationExecutionNode or OperationBatchExecutionNode))
Expand All @@ -1138,7 +1140,7 @@ private static void WireOperationDependencies(ExecutionPlanBuildContext ctx)
}

// For a standalone operation node, attach dependencies directly.
foreach (var dependencyId in stepDependencies)
foreach (var dependencyId in stepDependencies.Order())
{
if (!ctx.ExecutionNodes.TryGetValue(dependencyId, out var childEntry)
|| childEntry is not (
Expand All @@ -1163,7 +1165,7 @@ private static void WireBatchNodeDependencies(
{
var seenExecutionDependencies = new HashSet<int>();

foreach (var dependencyId in stepDependencies)
foreach (var dependencyId in stepDependencies.Order())
{
if (dependencyId == batchEntry.Id)
{
Expand Down Expand Up @@ -1199,7 +1201,7 @@ private static void WireBatchNodeDependencies(

private static void WireNodeFieldBranchesAndFallbacks(ExecutionPlanBuildContext ctx)
{
foreach (var (nodeId, branches) in ctx.BranchesByNodeId)
foreach (var (nodeId, branches) in ctx.BranchesByNodeId.OrderBy(static t => t.Key))
{
if (!ctx.ExecutionNodes.TryGetValue(nodeId, out var entry) || entry is not NodeFieldExecutionNode node)
{
Expand All @@ -1215,7 +1217,7 @@ private static void WireNodeFieldBranchesAndFallbacks(ExecutionPlanBuildContext
}
}

foreach (var (nodeId, fallbackNodeId) in ctx.FallbackByNodeId)
foreach (var (nodeId, fallbackNodeId) in ctx.FallbackByNodeId.OrderBy(static t => t.Key))
{
if (!ctx.ExecutionNodes.TryGetValue(nodeId, out var entry) || entry is not NodeFieldExecutionNode node)
{
Expand Down Expand Up @@ -1267,7 +1269,7 @@ internal static Dictionary<int, int> CreateBatchingGroupLookup(
var dependencyDepthLookup = new Dictionary<int, int>();
var recursionStack = new HashSet<int>();

foreach (var serviceSteps in queryStepsByService.Values)
foreach (var (_, serviceSteps) in queryStepsByService.OrderBy(static t => t.Key, StringComparer.Ordinal))
{
foreach (var step in serviceSteps)
{
Expand Down Expand Up @@ -1345,7 +1347,7 @@ private static int GetDependencyDepth(

var maxDepth = 0;

foreach (var dependency in directDependencies.OrderBy(t => t))
foreach (var dependency in directDependencies)
{
var dependencyDepth = GetDependencyDepth(
dependency,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ private ImmutableList<PlanStep> ApplyDeferRequirementsToParent(

// Collect steps that depend directly on the initial requirement step.
var downstreamByStepId = new Dictionary<int, OperationPlanStep>();
foreach (var dependentStepId in selfFetch.Dependents)
foreach (var dependentStepId in selfFetch.Dependents.Order())
{
if (incrementalPlanSteps.ById(dependentStepId) is OperationPlanStep dependentStep)
{
Expand Down
34 changes: 23 additions & 11 deletions src/HotChocolate/Fusion/src/Fusion.Execution/Planning/PlanQueue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,9 @@ private void EnqueueLookupPlanNodes(
continue;
}

if (schema.TryGetBestDirectLookup(
type,
allCandidateSchemas.Remove(toSchema),
toSchema,
out var bestLookup))
var fromSchemas = GetTransitionSourceSchemas(allCandidateSchemas, toSchema, workItem);

if (schema.TryGetBestDirectLookup(type, fromSchemas, toSchema, out var bestLookup))
{
var lookupWorkItem = workItem with { Lookup = bestLookup };
var branchBacklog = backlog.Push(lookupWorkItem);
Expand Down Expand Up @@ -244,7 +242,7 @@ private bool TryEnqueueConcreteTypeLookupPlanNodes(
}

var branchBacklog = backlog;
var fromSchemas = allCandidateSchemas.Remove(toSchema);
var fromSchemas = GetTransitionSourceSchemas(allCandidateSchemas, toSchema, workItem);
var allFound = true;

// for each concrete type that implements the abstract type,
Expand Down Expand Up @@ -332,11 +330,10 @@ private bool TryEnqueueConcreteTypeLookupPlanNodes(
continue;
}

if (schema.TryGetBestDirectLookup(
possibleType,
allCandidateSchemas.Remove(candidateSchema),
candidateSchema,
out var directLookup))
var fromSchemas =
GetTransitionSourceSchemas(allCandidateSchemas, candidateSchema, workItem);

if (schema.TryGetBestDirectLookup(possibleType, fromSchemas, candidateSchema, out var directLookup))
{
concreteLookup = directLookup;
lookupSchema = candidateSchema;
Expand Down Expand Up @@ -401,6 +398,21 @@ private static double EstimateRemainingCost(PlanNode planNodeTemplate, Backlog b
planNodeTemplate.OpsPerLevel,
branchBacklog.Cost);

private static ImmutableHashSet<string> GetTransitionSourceSchemas(
ImmutableHashSet<string> candidateSchemas,
string toSchema,
OperationWorkItem workItem)
{
var fromSchemas = candidateSchemas.Remove(toSchema);

if (!workItem.Dependents.IsEmpty && !string.IsNullOrEmpty(workItem.FromSchema))
{
fromSchemas = fromSchemas.Remove(workItem.FromSchema);
}

return fromSchemas;
}

private double GetResolutionCost(SelectionSet selectionSet, string schemaName)
{
foreach (var (candidateSchema, candidateCost) in schema.GetPossibleSchemas(selectionSet))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Collections.Immutable;
using HotChocolate.Collections.Immutable;
using HotChocolate.Fusion.Execution.Nodes;
using HotChocolate.Language;

Expand All @@ -14,10 +14,10 @@ public record NodeFieldPlanStep : PlanStep

public required OperationPlanStep FallbackQuery { get; init; }

public ImmutableDictionary<string, OperationPlanStep> Branches { get; set; }
public ImmutableOrderedDictionary<string, OperationPlanStep> Branches { get; set; }
#if NET10_0_OR_GREATER
= [];
#else
= ImmutableDictionary<string, OperationPlanStep>.Empty;
= ImmutableOrderedDictionary<string, OperationPlanStep>.Empty;
#endif
}
Loading
Loading