protected override async Task ExecuteNodeTreeAsync(ExecutionContext context, ObjectExecutionNode rootNode) { var pendingNodes = new List <ExecutionNode> { rootNode }; while (pendingNodes.Count > 0) { var currentTasks = new Task <ExecutionNode> [pendingNodes.Count]; // Start executing all pending nodes for (int i = 0; i < pendingNodes.Count; i++) { currentTasks[i] = ExecuteNodeAsync(context, pendingNodes[i]); } pendingNodes.Clear(); await OnBeforeExecutionStepAwaitedAsync(context) .ConfigureAwait(false); // Await tasks for this execution step var completedNodes = await Task.WhenAll(currentTasks) .ConfigureAwait(false); // Add child nodes to pending nodes to execute the next level in parallel var childNodes = completedNodes .OfType <IParentExecutionNode>() .SelectMany(x => x.GetChildNodes()); pendingNodes.AddRange(childNodes); } }
public static void SetSubFieldNodes(ExecutionContext context, ObjectExecutionNode parent, Dictionary <string, Field> fields) { var parentType = parent.GetObjectGraphType(context.Schema); var subFields = new Dictionary <string, ExecutionNode>(fields.Count); foreach (var kvp in fields) { var name = kvp.Key; var field = kvp.Value; if (!ShouldIncludeNode(context, field.Directives)) { continue; } var fieldDefinition = GetFieldDefinition(context.Document, context.Schema, parentType, field); if (fieldDefinition == null) { continue; } var node = BuildExecutionNode(parent, fieldDefinition.ResolvedType, field, fieldDefinition); if (node == null) { continue; } subFields[name] = node; } parent.SubFields = subFields; }
protected override async Task ExecuteNodeTreeAsync(ExecutionContext context, ObjectExecutionNode rootNode) { // Use a stack to track all nodes in the tree that need to be executed var nodes = new Stack <ExecutionNode>(); nodes.Push(rootNode); // Process each node on the stack one by one while (nodes.Count > 0) { var node = nodes.Pop(); var task = ExecuteNodeAsync(context, node); await OnBeforeExecutionStepAwaitedAsync(context) .ConfigureAwait(false); await task.ConfigureAwait(false); // Push any child nodes on top of the stack if (node is IParentExecutionNode parentNode) { // Add in reverse order so fields are executed in the correct order foreach (var child in parentNode.GetChildNodes().Reverse()) { nodes.Push(child); } } } }
protected override async Task ExecuteNodeTreeAsync(ExecutionContext context, ObjectExecutionNode rootNode) { //Options var blockOptions = new ExecutionDataflowBlockOptions { CancellationToken = context.CancellationToken, MaxDegreeOfParallelism = 1024 }; //Execute Block var block = new TransformManyBlock <Job, Job>( async job => { var node = await ExecuteNodeAsync(job.Context, job.Node).ConfigureAwait(false); var childJobs = (job.Node as IParentExecutionNode)? .GetChildNodes() .Select(child => job.CreateChildJob(job.Context, child)) .ToArray(); job.Complete(); return(childJobs); }, blockOptions); //Link to self block.LinkTo(block); //Start var rootJob = new Job(context, rootNode); await block.SendAsync(rootJob); //Wait until done await rootJob.Completion; }
/// <summary> /// Creates specified child execution nodes of an object execution node. /// </summary> private static void SetSubFieldNodes(ExecutionContext context, ObjectExecutionNode parent, Fields fields) { var parentType = parent.GetObjectGraphType(context.Schema); var subFields = new ExecutionNode[fields.Count]; int i = 0; foreach (var kvp in fields) { var field = kvp.Value; var fieldDefinition = ExecutionHelper.GetFieldDefinition(context.Schema, parentType, field); if (fieldDefinition == null) { throw new InvalidOperationException($"Schema is not configured correctly to fetch field '{field.Name}' from type '{parentType.Name}'."); } var node = BuildExecutionNode(parent, fieldDefinition.ResolvedType, field, fieldDefinition); subFields[i++] = node; } parent.SubFields = subFields; }
public static void SetSubFieldNodes(ExecutionContext context, ObjectExecutionNode parent) { var fields = new Dictionary <string, Field>(); var visitedFragments = new List <string>(); fields = CollectFields(context, parent.GetObjectGraphType(), parent.Field?.SelectionSet, fields, visitedFragments); SetSubFieldNodes(context, parent, fields); }
/// <summary> /// Creates execution nodes for child fields of an object execution node. Only run if /// the object execution node result is not null. /// </summary> private static void SetSubFieldNodes(ExecutionContext context, ObjectExecutionNode parent) { var fields = System.Threading.Interlocked.Exchange(ref context.ReusableFields, null) ?? new Fields(); SetSubFieldNodes(context, parent, fields.CollectFrom(context, parent.GetObjectGraphType(context.Schema), parent.Field?.SelectionSet)); fields.Clear(); System.Threading.Interlocked.CompareExchange(ref context.ReusableFields, fields, null); }
public static void SetSubFieldNodes(ExecutionContext context, ObjectExecutionNode parent) { var fields = CollectFields(context, parent.GetObjectGraphType(context.Schema), parent.Field?.SelectionSet); SetSubFieldNodes(context, parent, fields); }
protected abstract Task ExecuteNodeTreeAsync(ExecutionContext context, ObjectExecutionNode rootNode);
protected override Task ExecuteNodeTreeAsync(ExecutionContext context, ObjectExecutionNode rootNode) => ExecuteNodeTreeAsync(context, rootNode);
/// <summary> /// Executes document nodes serially. Nodes that return a <see cref="IDataLoaderResult"/> will /// execute once all other pending nodes have been completed. /// </summary> protected override async Task ExecuteNodeTreeAsync(ExecutionContext context, ObjectExecutionNode rootNode) { // Use a stack to track all nodes in the tree that need to be executed var nodes = System.Threading.Interlocked.Exchange(ref _reusableNodes, null) ?? new Stack <ExecutionNode>(); nodes.Push(rootNode); var dataLoaderNodes = System.Threading.Interlocked.Exchange(ref _reusableDataLoaderNodes, null) ?? new Queue <ExecutionNode>(); try { // Process each node on the stack one by one while (nodes.Count > 0 || dataLoaderNodes.Count > 0) { while (nodes.Count > 0) { var node = nodes.Pop(); var task = ExecuteNodeAsync(context, node); #pragma warning disable CS0612 // Type or member is obsolete await OnBeforeExecutionStepAwaitedAsync(context) #pragma warning restore CS0612 // Type or member is obsolete .ConfigureAwait(false); await task.ConfigureAwait(false); // Push any child nodes on top of the stack if (node.Result is IDataLoaderResult) { dataLoaderNodes.Enqueue(node); } else if (node is IParentExecutionNode parentNode) { // Add in reverse order so fields are executed in the correct order parentNode.ApplyToChildren((node, state) => state.Push(node), nodes, reverse: true); } } while (dataLoaderNodes.Count > 0) { var node = dataLoaderNodes.Dequeue(); var task = CompleteDataLoaderNodeAsync(context, node); #pragma warning disable CS0612 // Type or member is obsolete await OnBeforeExecutionStepAwaitedAsync(context) #pragma warning restore CS0612 // Type or member is obsolete .ConfigureAwait(false); await task.ConfigureAwait(false); // Push any child nodes on top of the stack if (node.Result is IDataLoaderResult) { dataLoaderNodes.Enqueue(node); } else if (node is IParentExecutionNode parentNode) { // Add in reverse order so fields are executed in the correct order parentNode.ApplyToChildren((node, state) => state.Push(node), nodes, reverse: true); } } } } finally { nodes.Clear(); dataLoaderNodes.Clear(); System.Threading.Interlocked.CompareExchange(ref _reusableNodes, nodes, null); System.Threading.Interlocked.CompareExchange(ref _reusableDataLoaderNodes, dataLoaderNodes, null); } }
/// <summary> /// Executes document nodes serially. Nodes that return a <see cref="IDataLoaderResult"/> will /// execute once all other pending nodes have been completed. /// </summary> protected override async Task ExecuteNodeTreeAsync(ExecutionContext context, ObjectExecutionNode rootNode) { // Use a stack to track all nodes in the tree that need to be executed var nodes = new Stack <ExecutionNode>(); nodes.Push(rootNode); var dataLoaderNodes = new Queue <ExecutionNode>(); // Process each node on the stack one by one while (nodes.Count > 0 || dataLoaderNodes.Count > 0) { while (nodes.Count > 0) { var node = nodes.Pop(); var task = ExecuteNodeAsync(context, node); #pragma warning disable CS0612 // Type or member is obsolete await OnBeforeExecutionStepAwaitedAsync(context) #pragma warning restore CS0612 // Type or member is obsolete .ConfigureAwait(false); await task.ConfigureAwait(false); // Push any child nodes on top of the stack if (node.Result is IDataLoaderResult) { dataLoaderNodes.Enqueue(node); } else if (node is IParentExecutionNode parentNode) { // Add in reverse order so fields are executed in the correct order foreach (var child in parentNode.GetChildNodes().Reverse()) { nodes.Push(child); } } } while (dataLoaderNodes.Count > 0) { var node = dataLoaderNodes.Dequeue(); var task = CompleteDataLoaderNodeAsync(context, node); #pragma warning disable CS0612 // Type or member is obsolete await OnBeforeExecutionStepAwaitedAsync(context) #pragma warning restore CS0612 // Type or member is obsolete .ConfigureAwait(false); await task.ConfigureAwait(false); // Push any child nodes on top of the stack if (node.Result is IDataLoaderResult) { dataLoaderNodes.Enqueue(node); } else if (node is IParentExecutionNode parentNode) { // Add in reverse order so fields are executed in the correct order foreach (var child in parentNode.GetChildNodes().Reverse()) { nodes.Push(child); } } } } }
/// <summary> /// Executes document nodes serially. Nodes that return a <see cref="IDataLoaderResult"/> will /// execute once all other pending nodes have been completed. /// </summary> protected override async Task ExecuteNodeTreeAsync(ExecutionContext context, ObjectExecutionNode rootNode) { async Task AwaitSafe(Task task) { try { #pragma warning disable CS0612 // Type or member is obsolete await OnBeforeExecutionStepAwaitedAsync(context) #pragma warning restore CS0612 // Type or member is obsolete .ConfigureAwait(false); } catch (Exception original) { try { await task.ConfigureAwait(false); } catch (Exception awaited) { if (original.Data?.IsReadOnly == false) { original.Data["GRAPHQL_TASK_AWAITED_EXCEPTION"] = awaited; } } throw; } await task.ConfigureAwait(false); } // Use a stack to track all nodes in the tree that need to be executed var nodes = System.Threading.Interlocked.Exchange(ref _reusableNodes, null) ?? new Stack <ExecutionNode>(); nodes.Push(rootNode); var dataLoaderNodes = System.Threading.Interlocked.Exchange(ref _reusableDataLoaderNodes, null) ?? new Queue <ExecutionNode>(); var addlNodes = System.Threading.Interlocked.Exchange(ref _reusableAddlNodes, null) ?? new Stack <ExecutionNode>(); try { // Process each node on the stack one by one while (nodes.Count > 0 || dataLoaderNodes.Count > 0) { while (nodes.Count > 0) { var node = nodes.Pop(); var task = ExecuteNodeAsync(context, node); await AwaitSafe(task).ConfigureAwait(false); // Push any child nodes on top of the stack if (node.Result is IDataLoaderResult) { dataLoaderNodes.Enqueue(node); } else if (node is IParentExecutionNode parentNode) { // Add in reverse order so fields are executed in the correct order parentNode.ApplyToChildren((node, state) => state.Push(node), nodes, reverse: true); } } while (dataLoaderNodes.Count > 0) { var node = dataLoaderNodes.Dequeue(); var task = CompleteDataLoaderNodeAsync(context, node); await AwaitSafe(task).ConfigureAwait(false); // Push any child nodes on top of the stack if (node.Result is IDataLoaderResult) { dataLoaderNodes.Enqueue(node); } else if (node is IParentExecutionNode parentNode) { // Do not reverse the order of the nodes here parentNode.ApplyToChildren((node, state) => state.Push(node), addlNodes, reverse: false); } } // Reverse order of queued nodes from data loader nodes so they are executed in the correct order while (addlNodes.Count > 0) { nodes.Push(addlNodes.Pop()); } } } finally { nodes.Clear(); dataLoaderNodes.Clear(); addlNodes.Clear(); System.Threading.Interlocked.CompareExchange(ref _reusableNodes, nodes, null); System.Threading.Interlocked.CompareExchange(ref _reusableDataLoaderNodes, dataLoaderNodes, null); System.Threading.Interlocked.CompareExchange(ref _reusableAddlNodes, addlNodes, null); } }