Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Concurrency issue on DataLoader selector #8081

Open
Deukhoofd opened this issue Feb 28, 2025 · 0 comments
Open

Concurrency issue on DataLoader selector #8081

Deukhoofd opened this issue Feb 28, 2025 · 0 comments

Comments

@Deukhoofd
Copy link

Product

Hot Chocolate

Version

15.0.3

Link to minimal reproduction

https://github.com/Deukhoofd/HotChocolateConcurrencyBug

Steps to reproduce

Start all three services, send the following request to the gateway at http://localhost:5001/graphql/

{
  primaryModel(id: 1) {
    id
    value
    secondary {
      name
      tertiary {
        name
      }
    }
    anotherSecondary {
      name
      tertiary {
        name
      }
    }
  }
}

We use Fusion here to first get the primaryModel from exampleService1, then two separate selections of secondaryModel, which get sent as 2 separate requests to exampleService2 at approximately the same time.

What is expected?

We get a result without any errors

What is actually happening?

We encounter an exception on (only) the very first request to ExampleService2. Any subsequent requests will return as expected.

Relevant log output

An item with the same key has already been added. Key:
        ExampleService1.TertiaryModel

           at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue
        value, InsertionBehavior behavior)
           at System.Reflection.NullabilityInfoContext.GetNullableContext(MemberInfo memberInfo)
           at System.Reflection.NullabilityInfoContext.GetNullabilityInfo(MemberInfo memberInfo, Type type, NullableAttributeStateParser parser, Int32& index)
           at System.Reflection.NullabilityInfoContext.GetNullabilityInfo(MemberInfo memberInfo, Type type, NullableAttributeStateParser parser)
           at System.Reflection.NullabilityInfoContext.Create(PropertyInfo propertyInfo)
           at HotChocolate.Execution.Projections.SelectionExpressionBuilder.IsNullableType(PropertyInfo propertyInfo)
           at HotChocolate.Execution.Projections.SelectionExpressionBuilder.BuildAssignmentExpression(PropertyNode node, Context context)
           at HotChocolate.Execution.Projections.SelectionExpressionBuilder.BuildSelectionSetExpression(Context context, TypeNode parent)
           at HotChocolate.Execution.Projections.SelectionExpressionBuilder.BuildTypeSwitchExpression(Context context, TypeContainer parent)
           at HotChocolate.Execution.Projections.SelectionExpressionBuilder.BuildAssignmentExpression(PropertyNode node, Context context)
           at HotChocolate.Execution.Projections.SelectionExpressionBuilder.BuildSelectionSetExpression(Context context, TypeNode parent)
           at HotChocolate.Execution.Projections.SelectionExpressionBuilder.BuildTypeSwitchExpression(Context context, TypeContainer parent)
           at HotChocolate.Execution.Projections.SelectionExpressionBuilder.BuildExpression[TRoot](ISelection selection)
           at HotChocolate.Execution.Processing.HotChocolateExecutionSelectionExtensions.<>c__2`1.<GetOrCreateExpression>b__2_0(String _, ValueTuple`2 ctx)
           at HotChocolate.Execution.Processing.Operation.GetOrAddState[TState,TContext](String key, Func`3 createState, TContext context)
           at HotChocolate.Execution.Processing.HotChocolateExecutionSelectionExtensions.GetOrCreateExpression[TValue](ISelection selection)
           at HotChocolate.Execution.Processing.HotChocolateExecutionSelectionExtensions.AsSelector[TValue](ISelection selection)
           at GreenDonut.Data.HotChocolateExecutionDataLoaderExtensions.Select[TKey,TValue](IDataLoader`2 dataLoader, ISelection selection)
           at ExampleService2.Query.GetSecondaryModelByIds(IReadOnlyList`1 ids, ISecondaryModelsDataLoader dataLoader, ISelection selectorBuilder, CancellationToken cancellationToken) in /home/nathan/Projects/HotChocolateConcurrencyBug/HotChocolateConcurrencyBug/ExampleService2/Query.cs:line 17
           at HotChocolate.Resolvers.Expressions.ExpressionHelper.AwaitTaskHelper[T](Task`1 task)
           at HotChocolate.Types.Helpers.FieldMiddlewareCompiler.<>c__DisplayClass9_0.<<CreateResolverMiddleware>b__0>d.MoveNext()
        --- End of stack trace from previous location ---
           at HotChocolate.Execution.Processing.Tasks.ResolverTask.ExecuteResolverPipelineAsync(CancellationToken cancellationToken)
           at HotChocolate.Execution.Processing.Tasks.ResolverTask.TryExecuteAsync(CancellationToken cancellationToken)

Additional context

This appears to be related to this specific NullabilityInfoContext, which is a static, and can be used across multiple threads:

private static readonly NullabilityInfoContext _nullabilityInfoContext = new();

NullabilityInfoContext is however not thread-safe, and should not be used in such a manner (dotnet/runtime#100254)

@Deukhoofd Deukhoofd changed the title ConcurrencyIssue on DataLoader selector Concurrency issue on DataLoader selector Feb 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant