From 2388fae0e9cbca9e5c5e1092624e7d8b7c633113 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Tue, 31 Oct 2023 20:57:56 +0100 Subject: [PATCH 1/2] Added build --- .github/workflows/release.yml | 54 +++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000000..70f0af46aea --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,54 @@ +name: Release + +on: + push: + tags: + - '12.*' + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: | + 6.x + 7.x + 8.x + + - name: Get the version + id: get_version + run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV + + - name: Build Packages + run: | + ./build.sh pack --SemVersion ${{ env.GIT_TAG }} --Configuration Release + + - name: Push Packages + run: | + ./build.cmd publish --skip + env: + NuGetApiKey: ${{ secrets.NUGETAPIKEY }} + + - name: Get release + id: get_release + run: | + RELEASE_ID=$(gh api repos/ChilliCream/graphql-platform/releases/tags/${{ env.GIT_TAG }} --jq '.id') + UPLOAD_URL=$(gh api repos/ChilliCream/graphql-platform/releases/$RELEASE_ID --jq '.upload_url') + echo "RELEASE_ID=$RELEASE_ID" >> $GITHUB_ENV + echo "UPLOAD_URL=${UPLOAD_URL}" >> $GITHUB_ENV + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload Release Assets + run: | + for file in ./output/packages/*.nupkg; do + echo "Uploading $file" + gh release upload ${{ env.GIT_TAG }} "$file" --repo ChilliCream/graphql-platform + done + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From ab71c394d2199ea92453288cb66731639d086942 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Tue, 31 Oct 2023 21:48:33 +0100 Subject: [PATCH 2/2] Optimized Field Merging --- .../Types/Types/Extensions/TypeExtensions.cs | 46 +++- .../Validation/DocumentValidatorContext.cs | 6 + .../Core/src/Validation/ErrorHelper.cs | 2 +- .../Core/src/Validation/FieldInfo.cs | 38 ++- .../Core/src/Validation/FieldInfoPair.cs | 67 +++++ .../Validation/HotChocolate.Validation.csproj | 1 + .../Validation/IDocumentValidatorContext.cs | 15 + .../Core/src/Validation/Rules/FieldVisitor.cs | 257 ++++++++++++------ ...ntationTests.Track_data_loader_events.snap | 79 ++++++ 9 files changed, 425 insertions(+), 86 deletions(-) create mode 100644 src/HotChocolate/Core/src/Validation/FieldInfoPair.cs create mode 100644 src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_data_loader_events.snap diff --git a/src/HotChocolate/Core/src/Types/Types/Extensions/TypeExtensions.cs b/src/HotChocolate/Core/src/Types/Types/Extensions/TypeExtensions.cs index 868ce6fdcb5..b3370e65f47 100644 --- a/src/HotChocolate/Core/src/Types/Types/Extensions/TypeExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Types/Extensions/TypeExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; using HotChocolate.Language; using HotChocolate.Properties; using static HotChocolate.Utilities.ThrowHelper; @@ -190,6 +191,7 @@ public static bool IsNamedType(this IType type) } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool IsType(this IType type, TypeKind kind) { if (type.Kind == kind) @@ -197,7 +199,7 @@ internal static bool IsType(this IType type, TypeKind kind) return true; } - if (type.Kind == TypeKind.NonNull && ((NonNullType)type).Type.Kind == kind) + if (type.Kind == TypeKind.NonNull && ((NonNullType) type).Type.Kind == kind) { return true; } @@ -205,6 +207,48 @@ internal static bool IsType(this IType type, TypeKind kind) return false; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool IsType(this IType type, TypeKind kind1, TypeKind kind2) + { + if (type.Kind == kind1 || type.Kind == kind2) + { + return true; + } + + if (type.Kind == TypeKind.NonNull) + { + var innerKind = ((NonNullType) type).Type.Kind; + + if (innerKind == kind1 || innerKind == kind2) + { + return true; + } + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool IsType(this IType type, TypeKind kind1, TypeKind kind2, TypeKind kind3) + { + if (type.Kind == kind1 || type.Kind == kind2 || type.Kind == kind3) + { + return true; + } + + if (type.Kind == TypeKind.NonNull) + { + var innerKind = ((NonNullType) type).Type.Kind; + + if (innerKind == kind1 || innerKind == kind2 || innerKind == kind3) + { + return true; + } + } + + return false; + } + public static IType InnerType(this IType type) { if (type is null) diff --git a/src/HotChocolate/Core/src/Validation/DocumentValidatorContext.cs b/src/HotChocolate/Core/src/Validation/DocumentValidatorContext.cs index 24c41201aa9..8dc62791171 100644 --- a/src/HotChocolate/Core/src/Validation/DocumentValidatorContext.cs +++ b/src/HotChocolate/Core/src/Validation/DocumentValidatorContext.cs @@ -100,6 +100,12 @@ public IOutputType NonNullString public IDictionary ContextData { get; set; } = default!; + public List CurrentFieldPairs { get; } = new(); + + public List NextFieldPairs { get; } = new(); + + public HashSet ProcessedFieldPairs { get; } = new(); + public IList RentFieldInfoList() { FieldInfoListBuffer buffer = _buffers.Peek(); diff --git a/src/HotChocolate/Core/src/Validation/ErrorHelper.cs b/src/HotChocolate/Core/src/Validation/ErrorHelper.cs index 03ccb2f51dc..6aeeeb84760 100644 --- a/src/HotChocolate/Core/src/Validation/ErrorHelper.cs +++ b/src/HotChocolate/Core/src/Validation/ErrorHelper.cs @@ -261,7 +261,7 @@ public static IError FieldIsRequiredButNull( .Build(); } - public static IError FieldsAreNotMergable( + public static IError FieldsAreNotMergeable( this IDocumentValidatorContext context, FieldInfo fieldA, FieldInfo fieldB) diff --git a/src/HotChocolate/Core/src/Validation/FieldInfo.cs b/src/HotChocolate/Core/src/Validation/FieldInfo.cs index 80e770d06b1..bfa296a838a 100644 --- a/src/HotChocolate/Core/src/Validation/FieldInfo.cs +++ b/src/HotChocolate/Core/src/Validation/FieldInfo.cs @@ -1,3 +1,4 @@ +using System; using HotChocolate.Language; using HotChocolate.Types; @@ -7,7 +8,7 @@ namespace HotChocolate.Validation; /// The validation field info provides access to the field node and the type /// information of the referenced field. /// -public readonly struct FieldInfo +public readonly struct FieldInfo : IEquatable { /// /// Initializes a new instance of @@ -41,4 +42,39 @@ public FieldInfo(IType declaringType, IType type, FieldNode field) /// Gets the field selection. /// public FieldNode Field { get; } + + /// + /// Compares this field info to another field info. + /// + /// + /// The other field info. + /// + /// + /// true if the field infos are equal. + /// + public bool Equals(FieldInfo other) + => Field.Equals(other.Field) && + DeclaringType.Equals(other.DeclaringType) && + Type.Equals(other.Type); + + /// + /// Compares this field info to another object. + /// + /// + /// The other object. + /// + /// + /// true if the field infos are equal. + /// + public override bool Equals(object? obj) + => obj is FieldInfo other && Equals(other); + + /// + /// Returns the hash code of this instance. + /// + /// + /// The hash code of this instance. + /// + public override int GetHashCode() + => HashCode.Combine(Field, DeclaringType, Type); } diff --git a/src/HotChocolate/Core/src/Validation/FieldInfoPair.cs b/src/HotChocolate/Core/src/Validation/FieldInfoPair.cs new file mode 100644 index 00000000000..566933be8ae --- /dev/null +++ b/src/HotChocolate/Core/src/Validation/FieldInfoPair.cs @@ -0,0 +1,67 @@ +using System; + +namespace HotChocolate.Validation; + +/// +/// Represents a pair of field infos. +/// +public readonly struct FieldInfoPair : IEquatable +{ + /// + /// Initializes a new instance of . + /// + /// + /// The first field info. + /// + /// + /// The second field info. + /// + public FieldInfoPair(FieldInfo fieldA, FieldInfo fieldB) + { + FieldA = fieldA; + FieldB = fieldB; + } + + /// + /// Gets the first field info. + /// + public FieldInfo FieldA { get; } + + /// + /// Gets the second field info. + /// + public FieldInfo FieldB { get; } + + /// + /// Compares this field info pair to another field info pair. + /// + /// + /// The other field info pair. + /// + /// + /// true if the field info pairs are equal. + /// + public bool Equals(FieldInfoPair other) + => FieldA.Equals(other.FieldA) && FieldB.Equals(other.FieldB); + + /// + /// Compares this field info pair to another object. + /// + /// + /// The other object. + /// + /// + /// true if the field info pairs are equal. + /// + public override bool Equals(object? obj) + => obj is FieldInfoPair other && Equals(other); + + /// + /// Returns the hash code for this field info pair. + /// + /// + /// The hash code for this field info pair. + /// + public override int GetHashCode() + => HashCode.Combine(FieldA, FieldB); +} diff --git a/src/HotChocolate/Core/src/Validation/HotChocolate.Validation.csproj b/src/HotChocolate/Core/src/Validation/HotChocolate.Validation.csproj index 153e916d13e..192033e91f4 100644 --- a/src/HotChocolate/Core/src/Validation/HotChocolate.Validation.csproj +++ b/src/HotChocolate/Core/src/Validation/HotChocolate.Validation.csproj @@ -22,6 +22,7 @@ + diff --git a/src/HotChocolate/Core/src/Validation/IDocumentValidatorContext.cs b/src/HotChocolate/Core/src/Validation/IDocumentValidatorContext.cs index a82672ac931..15486932a79 100644 --- a/src/HotChocolate/Core/src/Validation/IDocumentValidatorContext.cs +++ b/src/HotChocolate/Core/src/Validation/IDocumentValidatorContext.cs @@ -145,6 +145,21 @@ public interface IDocumentValidatorContext : ISyntaxVisitorContext /// IDictionary ContextData { get; } + /// + /// When processing field merging this list holds the field pairs that are processed. + /// + List CurrentFieldPairs { get; } + + /// + /// When processing field merging this list holds the field pairs that are processed next. + /// + List NextFieldPairs { get; } + + /// + /// When processing field merging this set represents the already processed field pairs. + /// + HashSet ProcessedFieldPairs { get; } + /// /// Rents a list of field infos. /// diff --git a/src/HotChocolate/Core/src/Validation/Rules/FieldVisitor.cs b/src/HotChocolate/Core/src/Validation/Rules/FieldVisitor.cs index 118727cebb2..4a6baff3e39 100644 --- a/src/HotChocolate/Core/src/Validation/Rules/FieldVisitor.cs +++ b/src/HotChocolate/Core/src/Validation/Rules/FieldVisitor.cs @@ -1,9 +1,12 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using HotChocolate.Language; using HotChocolate.Language.Visitors; using HotChocolate.Types; using HotChocolate.Types.Introspection; +using HotChocolate.Utilities; namespace HotChocolate.Validation.Rules; @@ -42,17 +45,26 @@ protected override ISyntaxVisitorAction Leave( { if (context.FieldSets.Count > 0) { - foreach (SelectionSetNode selectionSet in context.FieldSets.Keys) + foreach (var item in context.FieldSets) { - TryMergeFieldsInSet(context, context.FieldSets[selectionSet]); + TryMergeFieldsInSet(context, item.Value); + } + + var next = context.NextFieldPairs; + + while (next.Count > 0) + { + FillCurrentFieldPairs(context); + ProcessCurrentFieldPairs(context); } } if (node.SelectionSet.Selections.Count == 0) { - context.ReportError(context.NoSelectionOnRootType( - node, - context.Schema.GetOperationType(node.Operation)!)); + context.ReportError( + context.NoSelectionOnRootType( + node, + context.Schema.GetOperationType(node.Operation)!)); } return base.Leave(node, context); @@ -62,23 +74,24 @@ protected override ISyntaxVisitorAction Enter( FieldNode node, IDocumentValidatorContext context) { - SelectionSetNode selectionSet = context.SelectionSets.Peek(); - if (!context.FieldSets.TryGetValue(selectionSet, out IList? fields)) + var selectionSet = context.SelectionSets.Peek(); + + if (!context.FieldSets.TryGetValue(selectionSet, out var fields)) { fields = context.RentFieldInfoList(); context.FieldSets.Add(selectionSet, fields); } - if (IntrospectionFields.TypeName.Equals(node.Name.Value)) + if (IntrospectionFields.TypeName.Value.EqualsOrdinal(node.Name.Value)) { fields.Add(new FieldInfo(context.Types.Peek(), context.NonNullString, node)); return Skip; } - if (context.Types.TryPeek(out IType? type) && + if (context.Types.TryPeek(out var type) && type.NamedType() is IComplexOutputType ct) { - if (ct.Fields.TryGetField(node.Name.Value, out IOutputField? of)) + if (ct.Fields.TryGetField(node.Name.Value, out var of)) { fields.Add(new FieldInfo(context.Types.Peek(), of.Type, node)); @@ -134,12 +147,10 @@ protected override ISyntaxVisitorAction Enter( return Skip; } - if (context.Path.TryPeek(out ISyntaxNode? parent)) + if (context.Path.TryPeek(out var parent) && + parent.Kind is SyntaxKind.OperationDefinition or SyntaxKind.Field) { - if (parent.Kind is SyntaxKind.OperationDefinition or SyntaxKind.Field) - { - context.SelectionSets.Push(node); - } + context.SelectionSets.Push(node); } return Continue; @@ -149,12 +160,14 @@ protected override ISyntaxVisitorAction Leave( SelectionSetNode node, IDocumentValidatorContext context) { - if (context.Path.TryPeek(out ISyntaxNode? parent)) + if (!context.Path.TryPeek(out var parent)) { - if (parent.Kind is SyntaxKind.OperationDefinition or SyntaxKind.Field) - { - context.SelectionSets.Pop(); - } + return Continue; + } + + if (parent.Kind is SyntaxKind.OperationDefinition or SyntaxKind.Field) + { + context.SelectionSets.Pop(); } return Continue; @@ -164,12 +177,10 @@ protected override ISyntaxVisitorAction VisitChildren( FragmentSpreadNode node, IDocumentValidatorContext context) { - if (context.Fragments.TryGetValue( - node.Name.Value, - out FragmentDefinitionNode? fragment) && + if (context.Fragments.TryGetValue(node.Name.Value, out var fragment) && context.VisitedFragments.Add(fragment.Name.Value)) { - ISyntaxVisitorAction result = Visit(fragment, node, context); + var result = Visit(fragment, node, context); context.VisitedFragments.Remove(fragment.Name.Value); if (result.IsBreak()) @@ -185,7 +196,8 @@ private static bool HasFields(SelectionSetNode selectionSet) { for (var i = 0; i < selectionSet.Selections.Count; i++) { - ISelectionNode selection = selectionSet.Selections[i]; + var selection = selectionSet.Selections[i]; + if (selection.Kind is SyntaxKind.Field) { if (!IsTypeNameField(((FieldNode)selection).Name.Value)) @@ -197,60 +209,69 @@ private static bool HasFields(SelectionSetNode selectionSet) return false; } - private static bool IsTypeNameField(NameString fieldName) - { - return fieldName.Equals(IntrospectionFields.TypeName); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsTypeNameField(string fieldName) + => fieldName.EqualsOrdinal(IntrospectionFields.TypeName); private static void TryMergeFieldsInSet( IDocumentValidatorContext context, IList fields) { - if (fields.Count == 1) + while (true) { - if (fields[0].Field.SelectionSet is { } selectionSet && - context.FieldSets.TryGetValue(selectionSet, out IList? fieldSet)) + if (fields.Count == 1) { - TryMergeFieldsInSet(context, fieldSet); + if (fields[0].Field.SelectionSet is { } selectionSet && + context.FieldSets.TryGetValue(selectionSet, out var fieldSet)) + { + fields = fieldSet; + continue; + } + return; } + break; } - else + + for (var i = 0; i < fields.Count - 1; i++) { - for (var i = 0; i < fields.Count - 1; i++) + var fieldA = fields[i]; + + for (var j = i + 1; j < fields.Count; j++) { - FieldInfo fieldA = fields[i]; - for (var j = i + 1; j < fields.Count; j++) + var fieldB = fields[j]; + + if (ReferenceEquals(fieldA.Field, fieldB.Field) || + !fieldA.ResponseName.EqualsOrdinal(fieldB.ResponseName)) + { + continue; + } + + if (SameResponseShape( + fieldA.Type.RewriteNullability(fieldA.Field.Required), + fieldB.Type.RewriteNullability(fieldB.Field.Required))) { - FieldInfo fieldB = fields[j]; - if (!ReferenceEquals(fieldA.Field, fieldB.Field) && - string.Equals( - fieldA.ResponseName, - fieldB.ResponseName, - StringComparison.Ordinal)) + if (!IsParentTypeAligned(fieldA, fieldB)) { - if (SameResponseShape( - fieldA.Type.RewriteNullability(fieldA.Field.Required), - fieldB.Type.RewriteNullability(fieldB.Field.Required))) - { - if (IsParentTypeAligned(fieldA, fieldB)) - { - if (fieldA.Field.Name.Equals(fieldB.Field.Name) && - AreArgumentsIdentical(fieldA.Field, fieldB.Field)) - { - TryMergeFieldsInSet(context, fieldA, fieldB); - } - else if (context.FieldTuples.Add((fieldA.Field, fieldB.Field))) - { - context.ReportError( - context.FieldsAreNotMergable(fieldA, fieldB)); - } - } - } - else if (context.FieldTuples.Add((fieldA.Field, fieldB.Field))) + continue; + } + + if (fieldA.Field.Name.Value.EqualsOrdinal(fieldB.Field.Name.Value) && + AreArgumentsIdentical(fieldA.Field, fieldB.Field)) + { + var pair = new FieldInfoPair(fieldA, fieldB); + if (context.ProcessedFieldPairs.Add(pair)) { - context.ReportError(context.FieldsAreNotMergable(fieldA, fieldB)); + context.NextFieldPairs.Add(pair); } } + else if (context.FieldTuples.Add((fieldA.Field, fieldB.Field))) + { + context.ReportError(context.FieldsAreNotMergeable(fieldA, fieldB)); + } + } + else if (context.FieldTuples.Add((fieldA.Field, fieldB.Field))) + { + context.ReportError(context.FieldsAreNotMergeable(fieldA, fieldB)); } } } @@ -263,29 +284,41 @@ private static void TryMergeFieldsInSet( { if (fieldA.Field.SelectionSet is { } a && fieldB.Field.SelectionSet is { } b && - context.FieldSets.TryGetValue(a, out IList? al) && - context.FieldSets.TryGetValue(b, out IList? bl)) + context.FieldSets.TryGetValue(a, out var al) && + context.FieldSets.TryGetValue(b, out var bl)) { - IList mergedSet = context.RentFieldInfoList(); - CopyFieldInfos(al, mergedSet); - CopyFieldInfos(bl, mergedSet); + var mergedSet = Unsafe.As>(context.RentFieldInfoList()); +#if NET6_0_OR_GREATER + mergedSet.EnsureCapacity(al.Count + bl.Count); +#endif + CopyFieldInfos(Unsafe.As>(al), mergedSet); + CopyFieldInfos(Unsafe.As>(bl), mergedSet); TryMergeFieldsInSet(context, mergedSet); } } - private static void CopyFieldInfos(IList from, IList to) + private static void CopyFieldInfos(List from, List to) { - for (var i = 0; i < from.Count; i++) +#if NETSTANDARD2_0 || NETCOREAPP3_1 + foreach (var field in from) + { + to.Add(field); + } +#else + ref var field = ref MemoryMarshal.GetReference(CollectionsMarshal.AsSpan(from)); + ref var end = ref Unsafe.Add(ref field, from.Count); + + while (Unsafe.IsAddressLessThan(ref field, ref end)) { - to.Add(from[i]); + to.Add(field); + field = ref Unsafe.Add(ref field, 1); } +#endif } private static bool IsParentTypeAligned(FieldInfo fieldA, FieldInfo fieldB) - { - return ReferenceEquals(fieldA.DeclaringType, fieldB.DeclaringType) || + => ReferenceEquals(fieldA.DeclaringType, fieldB.DeclaringType) || !fieldA.DeclaringType.IsObjectType() && !fieldB.DeclaringType.IsObjectType(); - } private static bool AreArgumentsIdentical(FieldNode fieldA, FieldNode fieldB) { @@ -303,11 +336,13 @@ private static bool AreArgumentsIdentical(FieldNode fieldA, FieldNode fieldB) for (var i = 0; i < fieldA.Arguments.Count; i++) { - ArgumentNode argumentA = fieldA.Arguments[i]; + var argumentA = fieldA.Arguments[i]; + for (var j = 0; j < fieldB.Arguments.Count; j++) { - ArgumentNode argumentB = fieldB.Arguments[j]; - if (argumentA.Name.Value.Equals(argumentB.Name.Value, StringComparison.Ordinal)) + var argumentB = fieldB.Arguments[j]; + + if (argumentA.Name.Value.EqualsOrdinal(argumentB.Name.Value)) { if (argumentA.Value.Equals(argumentB.Value)) { @@ -325,9 +360,9 @@ private static bool SameResponseShape(IType typeA, IType typeB) { while (!typeA.IsNamedType() && !typeB.IsNamedType()) { - if (typeA.IsNonNullType() || typeB.IsNonNullType()) + if (typeA.Kind is TypeKind.NonNull || typeB.Kind is TypeKind.NonNull) { - if (typeA.IsNullableType() || typeB.IsNullableType()) + if (typeA.Kind is not TypeKind.NonNull || typeB.Kind is not TypeKind.NonNull) { return false; } @@ -336,9 +371,9 @@ private static bool SameResponseShape(IType typeA, IType typeB) typeB = typeB.InnerType(); } - if (typeA.IsListType() || typeB.IsListType()) + if (typeA.IsType(TypeKind.List) || typeB.IsType(TypeKind.List)) { - if (!typeA.IsListType() || !typeB.IsListType()) + if (!typeA.IsType(TypeKind.List) || !typeB.IsType(TypeKind.List)) { return false; } @@ -348,16 +383,72 @@ private static bool SameResponseShape(IType typeA, IType typeB) } } - if (typeA.IsLeafType() || typeB.IsLeafType()) + if (typeA.IsType(TypeKind.Scalar, TypeKind.Enum) || typeB.IsType(TypeKind.Scalar, TypeKind.Enum)) { return ReferenceEquals(typeA, typeB); } - if (typeA.IsCompositeType() && typeB.IsCompositeType()) + if (typeA.IsType(TypeKind.Object, TypeKind.Interface, TypeKind.Union) && + typeB.IsType(TypeKind.Object, TypeKind.Interface, TypeKind.Union)) { return true; } return false; } + + private static void FillCurrentFieldPairs(IDocumentValidatorContext context) + { + var next = context.NextFieldPairs; + var current = context.CurrentFieldPairs; + +#if NETSTANDARD2_0 || NETCOREAPP3_1 + foreach (var pair in next) + { + current.Add(pair); + } + + next.Clear(); +#else + ref var pair = ref MemoryMarshal.GetReference(CollectionsMarshal.AsSpan(next)); + ref var end = ref Unsafe.Add(ref pair, next.Count); + +#if NET6_0_OR_GREATER + current.EnsureCapacity(next.Count); +#endif + + while (Unsafe.IsAddressLessThan(ref pair, ref end)) + { + current.Add(pair); + pair = ref Unsafe.Add(ref pair, 1); + } + + next.Clear(); +#endif + } + + private static void ProcessCurrentFieldPairs(IDocumentValidatorContext context) + { + var current = context.CurrentFieldPairs; + +#if NETSTANDARD2_0 || NETCOREAPP3_1 + foreach (var pair in current) + { + TryMergeFieldsInSet(context, pair.FieldA, pair.FieldB); + } + + current.Clear(); +#else + ref var pair = ref MemoryMarshal.GetReference(CollectionsMarshal.AsSpan(current)); + ref var end = ref Unsafe.Add(ref pair, current.Count); + + while (Unsafe.IsAddressLessThan(ref pair, ref end)) + { + TryMergeFieldsInSet(context, pair.FieldA, pair.FieldB); + pair = ref Unsafe.Add(ref pair, 1); + } + + current.Clear(); +#endif + } } diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_data_loader_events.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_data_loader_events.snap new file mode 100644 index 00000000000..df5389e3c09 --- /dev/null +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/QueryInstrumentationTests.Track_data_loader_events.snap @@ -0,0 +1,79 @@ +{ + "activities": [ + { + "OperationName": "ValidateDocument", + "DisplayName": "Validate Document", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.document.id", + "Value": "myB0UQjI3lr8zDXNVurZ/A==" + }, + { + "Key": "graphql.document.hash", + "Value": "myB0UQjI3lr8zDXNVurZ/A==" + }, + { + "Key": "otel.status_code", + "Value": "OK" + } + ], + "event": [] + }, + { + "OperationName": "CompileOperation", + "DisplayName": "Compile Operation", + "Status": "Ok", + "tags": [ + { + "Key": "otel.status_code", + "Value": "OK" + } + ], + "event": [] + }, + { + "OperationName": "ResolveFieldValue", + "DisplayName": "/dataLoader", + "Status": "Ok", + "tags": [ + { + "Key": "graphql.selection.name", + "Value": "dataLoader" + }, + { + "Key": "graphql.selection.type", + "Value": "String!" + }, + { + "Key": "graphql.selection.path", + "Value": "/dataLoader" + }, + { + "Key": "graphql.selection.field.name", + "Value": "dataLoader" + }, + { + "Key": "graphql.selection.field.coordinate", + "Value": "SimpleQuery.dataLoader" + }, + { + "Key": "graphql.selection.field.declaringType", + "Value": "SimpleQuery" + }, + { + "Key": "otel.status_code", + "Value": "OK" + } + ], + "event": [] + }, + { + "OperationName": "ExecuteBatch", + "DisplayName": "Execute CustomDataLoader Batch", + "Status": "Unset", + "tags": [], + "event": [] + } + ] +}