Rework concurrency gate for subscriptions#9580
Conversation
There was a problem hiding this comment.
Pull request overview
This PR reworks concurrency limiting to operate at the GraphQL execution level (rather than HTTP request level), and extends the gate consistently to subscription handshakes and per-event executions across Hot Chocolate Core and Fusion.
Changes:
- Introduces
ExecutionConcurrencyGateplus aConcurrencyGateMiddlewareand wires it into the default execution pipelines. - Updates subscription execution paths (Core + Fusion) so each subscription event also acquires/releases a concurrency slot.
- Renames server option from
MaxConcurrentRequeststoMaxConcurrentExecutions, removes the old ASP.NET Core transport-level gate, and updates docs/tests accordingly.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| website/src/docs/hotchocolate/v16/migrating/migrate-from-15-to-16.md | Adds migration docs for the new execution-level concurrency gate. |
| website/src/docs/fusion/v16/performance-tuning.md | Updates Fusion perf docs to describe execution-based concurrency limiting and new option name. |
| src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Pipeline/FusionMiddleware.cs | Exposes the shared concurrency gate middleware config for Fusion pipelines. |
| src/HotChocolate/Fusion/src/Fusion.Execution/Execution/OperationPlanExecutor.cs | Gates subscription event processing with ExecutionConcurrencyGate. |
| src/HotChocolate/Fusion/src/Fusion.Execution/DependencyInjection/CoreFusionGatewayBuilderExtensions.Pipeline.cs | Adds UseConcurrencyGate() and inserts it into Fusion default pipelines. |
| src/HotChocolate/Fusion/src/Fusion.AspNetCore/DependencyInjection/FusionServerServiceCollectionExtensions.cs | Registers per-schema ExecutionConcurrencyGate based on named GraphQLServerOptions. |
| src/HotChocolate/Core/test/Execution.Tests/Pipeline/ConcurrencyGateMiddlewareTests.cs | Adds extensive tests for pipeline/event gating behavior and timeout semantics. |
| src/HotChocolate/Core/src/Types/Execution/Processing/SubscriptionExecutor.cs | Passes an optional gate into the subscription execution path. |
| src/HotChocolate/Core/src/Types/Execution/Processing/SubscriptionExecutor.Subscription.cs | Acquires/releases the gate per subscription event before invoking the query executor. |
| src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.UseRequest.cs | Adds UseConcurrencyGate() and inserts gate into configured pipelines. |
| src/HotChocolate/Core/src/Types/Execution/DependencyInjection/InternalSchemaServiceCollectionExtensions.cs | Injects ExecutionConcurrencyGate into SubscriptionExecutor via DI. |
| src/HotChocolate/Core/src/Execution.Pipeline/ConcurrencyGateMiddleware.cs | Adds execution-level concurrency middleware (skips entirely when disabled/unconfigured). |
| src/HotChocolate/Core/src/Execution.Pipeline/CommonMiddleware.cs | Exposes CommonMiddleware.ConcurrencyGate. |
| src/HotChocolate/Core/src/Execution.Abstractions/WellKnownRequestMiddleware.cs | Adds the well-known key for the new middleware. |
| src/HotChocolate/Core/src/Execution.Abstractions/Execution/ExecutionConcurrencyGate.cs | Implements the semaphore-based execution gate abstraction. |
| src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/HotChocolateAspNetCoreServiceCollectionExtensions.cs | Registers the per-schema ExecutionConcurrencyGate from GraphQLServerOptions. |
| src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Options/GraphQLServerOptions.cs | Renames MaxConcurrentRequests → MaxConcurrentExecutions. |
| src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/MiddlewareFactory.cs | Removes the old HTTP-level concurrency gate middleware. |
| src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Extensions/EndpointRouteBuilderExtensions.cs | Removes wiring of the old transport-level gate from endpoints. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (concurrencyGate is { IsEnabled: true }) | ||
| { | ||
| await concurrencyGate.WaitAsync(requestCancellationToken).ConfigureAwait(false); | ||
| gateAcquired = true; | ||
| } |
There was a problem hiding this comment.
CreateResponseStream waits on the concurrency gate using requestCancellationToken, but the event loop itself is driven by executionCancellationToken (which can be cancelled independently via the internal execution CTS). If the execution engine is cancelled (e.g. ExecutionState.CancelProcessing()), this wait can keep blocking even though the subscription execution should stop. Use executionCancellationToken (or a linked token of both) for WaitAsync(...) so gate waits abort when the execution is halted.
…ncy-gate # Conflicts: # src/HotChocolate/Fusion/src/Fusion.AspNetCore/DependencyInjection/FusionServerServiceCollectionExtensions.cs
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #9580 +/- ##
============================
============================
☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
No description provided.