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);
            }
        }
Esempio n. 2
0
        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;
        }
Esempio n. 3
0
        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);
                    }
                }
            }
        }
Esempio n. 4
0
        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);
        }
Esempio n. 8
0
        public static void SetSubFieldNodes(ExecutionContext context, ObjectExecutionNode parent)
        {
            var fields = CollectFields(context, parent.GetObjectGraphType(context.Schema), parent.Field?.SelectionSet);

            SetSubFieldNodes(context, parent, fields);
        }
Esempio n. 9
0
 protected abstract Task ExecuteNodeTreeAsync(ExecutionContext context, ObjectExecutionNode rootNode);
Esempio n. 10
0
 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);
            }
        }
Esempio n. 12
0
        /// <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);
                        }
                    }
                }
            }
        }
Esempio n. 13
0
        /// <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);
            }
        }