Skip to content

Commit dfb2fa2

Browse files
Make request executor creation eager by default (#8758)
1 parent 884a0e9 commit dfb2fa2

File tree

34 files changed

+658
-418
lines changed

34 files changed

+658
-418
lines changed

src/All.slnx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,8 @@
194194
<Project Path="HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/HotChocolate.Fusion.Execution.Tests.csproj" />
195195
<Project Path="HotChocolate/Fusion-vnext/test/Fusion.Language.Tests/HotChocolate.Fusion.Language.Tests.csproj" />
196196
<Project Path="HotChocolate/Fusion-vnext/test/Fusion.Packaging.Tests/HotChocolate.Fusion.Packaging.Tests.csproj" />
197-
<Project Path="HotChocolate/Fusion-vnext/test/Fusion.Utilities.Tests/HotChocolate.Fusion.Utilities.Tests.csproj" />
198197
<Project Path="HotChocolate/Fusion-vnext/test/Fusion.Tests.Shared/HotChocolate.Fusion.Tests.Shared.csproj" />
198+
<Project Path="HotChocolate/Fusion-vnext/test/Fusion.Utilities.Tests/HotChocolate.Fusion.Utilities.Tests.csproj" />
199199
</Folder>
200200
<Folder Name="/HotChocolate/Json/" />
201201
<Folder Name="/HotChocolate/Json/src/" />
Lines changed: 65 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,108 +1,115 @@
1+
using System.Diagnostics.CodeAnalysis;
12
using HotChocolate.AspNetCore.Warmup;
23
using HotChocolate.Execution.Configuration;
3-
using HotChocolate.Execution.Internal;
44

55
// ReSharper disable once CheckNamespace
66
namespace Microsoft.Extensions.DependencyInjection;
77

88
public static partial class HotChocolateAspNetCoreServiceCollectionExtensions
99
{
1010
/// <summary>
11-
/// Adds the current GraphQL configuration to the warmup background service.
11+
/// Adds a warmup task that will be executed on each newly created request executor.
1212
/// </summary>
1313
/// <param name="builder">
1414
/// The <see cref="IRequestExecutorBuilder"/>.
1515
/// </param>
16-
/// <param name="warmup">
17-
/// The warmup task that shall be executed on a new executor.
18-
/// </param>
19-
/// <param name="keepWarm">
20-
/// Apply warmup task after eviction and keep executor in-memory.
16+
/// <param name="warmupFunc">
17+
/// The warmup delegate to execute.
2118
/// </param>
2219
/// <param name="skipIf">
23-
/// Skips the warmup task if set to true.
20+
/// If <c>true</c>, the warmup task will not be registered.
2421
/// </param>
2522
/// <returns>
2623
/// Returns the <see cref="IRequestExecutorBuilder"/> so that configuration can be chained.
2724
/// </returns>
2825
/// <exception cref="ArgumentNullException">
29-
/// The <see cref="IRequestExecutorBuilder"/> is <c>null</c>.
26+
/// The <paramref name="builder"/> is <c>null</c>.
27+
/// </exception>
28+
/// <exception cref="ArgumentNullException">
29+
/// The <paramref name="warmupFunc"/> is <c>null</c>.
3030
/// </exception>
31-
public static IRequestExecutorBuilder InitializeOnStartup(
31+
public static IRequestExecutorBuilder AddWarmupTask(
3232
this IRequestExecutorBuilder builder,
33-
Func<IRequestExecutor, CancellationToken, Task>? warmup = null,
34-
bool keepWarm = false,
33+
Func<IRequestExecutor, CancellationToken, Task> warmupFunc,
3534
bool skipIf = false)
3635
{
3736
ArgumentNullException.ThrowIfNull(builder);
37+
ArgumentNullException.ThrowIfNull(warmupFunc);
3838

39-
if (!skipIf)
40-
{
41-
builder.Services.AddHostedService<RequestExecutorWarmupService>();
42-
builder.Services.AddSingleton(new WarmupSchemaTask(builder.Name, keepWarm, warmup));
43-
}
44-
45-
return builder;
39+
return builder.AddWarmupTask(new DelegateRequestExecutorWarmupTask(warmupFunc), skipIf);
4640
}
4741

4842
/// <summary>
49-
/// Adds the current GraphQL configuration to the warmup background service.
43+
/// Adds a warmup task that will be executed on each newly created request executor.
5044
/// </summary>
5145
/// <param name="builder">
5246
/// The <see cref="IRequestExecutorBuilder"/>.
5347
/// </param>
54-
/// <param name="options">
55-
/// The <see cref="RequestExecutorInitializationOptions"/>.
48+
/// <param name="warmupTask">
49+
/// The warmup task to execute.
5650
/// </param>
5751
/// <param name="skipIf">
58-
/// Skips the warmup task if set to true.
52+
/// If <c>true</c>, the warmup task will not be registered.
5953
/// </param>
6054
/// <returns>
6155
/// Returns the <see cref="IRequestExecutorBuilder"/> so that configuration can be chained.
6256
/// </returns>
6357
/// <exception cref="ArgumentNullException">
64-
/// The <see cref="IRequestExecutorBuilder"/> is <c>null</c>.
58+
/// The <paramref name="builder"/> is <c>null</c>.
6559
/// </exception>
66-
public static IRequestExecutorBuilder InitializeOnStartup(
60+
/// <exception cref="ArgumentNullException">
61+
/// The <paramref name="warmupTask"/> is <c>null</c>.
62+
/// </exception>
63+
public static IRequestExecutorBuilder AddWarmupTask(
6764
this IRequestExecutorBuilder builder,
68-
RequestExecutorInitializationOptions options,
65+
IRequestExecutorWarmupTask warmupTask,
6966
bool skipIf = false)
7067
{
7168
ArgumentNullException.ThrowIfNull(builder);
69+
ArgumentNullException.ThrowIfNull(warmupTask);
7270

7371
if (skipIf)
7472
{
7573
return builder;
7674
}
7775

78-
Func<IRequestExecutor, CancellationToken, Task>? warmup;
76+
return builder.ConfigureSchemaServices((_, sc) => sc.AddSingleton(warmupTask));
77+
}
7978

80-
if (options.WriteSchemaFile.Enable)
81-
{
82-
var schemaFileName =
83-
options.WriteSchemaFile.FileName
84-
?? System.IO.Path.Combine(Environment.CurrentDirectory, "schema.graphqls");
79+
/// <summary>
80+
/// Adds a warmup task for the request executor.
81+
/// </summary>
82+
/// <param name="builder">
83+
/// The <see cref="IRequestExecutorBuilder"/>.
84+
/// </param>
85+
/// <param name="skipIf">
86+
/// If <c>true</c>, the warmup task will not be registered.
87+
/// </param>
88+
/// <typeparam name="T">
89+
/// The warmup task to execute.
90+
/// </typeparam>
91+
/// <returns>
92+
/// Returns the <see cref="IRequestExecutorBuilder"/> so that configuration can be chained.
93+
/// </returns>
94+
/// <exception cref="ArgumentNullException">
95+
/// The <paramref name="builder"/> is <c>null</c>.
96+
/// </exception>
97+
public static IRequestExecutorBuilder AddWarmupTask<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(
98+
this IRequestExecutorBuilder builder,
99+
bool skipIf = false)
100+
where T : class, IRequestExecutorWarmupTask
101+
{
102+
ArgumentNullException.ThrowIfNull(builder);
85103

86-
if (options.Warmup is null)
87-
{
88-
warmup = async (executor, cancellationToken)
89-
=> await SchemaFileExporter.Export(schemaFileName, executor, cancellationToken);
90-
}
91-
else
92-
{
93-
warmup = async (executor, cancellationToken) =>
94-
{
95-
await SchemaFileExporter.Export(schemaFileName, executor, cancellationToken);
96-
await options.Warmup(executor, cancellationToken);
97-
};
98-
}
99-
}
100-
else
104+
if (skipIf)
101105
{
102-
warmup = options.Warmup;
106+
return builder;
103107
}
104108

105-
return InitializeOnStartup(builder, warmup, options.KeepWarm);
109+
builder.ConfigureSchemaServices(
110+
static (_, sc) => sc.AddSingleton<IRequestExecutorWarmupTask, T>());
111+
112+
return builder;
106113
}
107114

108115
/// <summary>
@@ -114,26 +121,24 @@ public static IRequestExecutorBuilder InitializeOnStartup(
114121
/// <param name="schemaFileName">
115122
/// The file name of the schema file.
116123
/// </param>
124+
/// <param name="skipIf">
125+
/// If <c>true</c>, the schema file will not be exported.
126+
/// </param>
117127
/// <returns>
118128
/// Returns the <see cref="IRequestExecutorBuilder"/> so that configuration can be chained.
119129
/// </returns>
120130
/// <exception cref="ArgumentNullException">
121-
/// The <see cref="IRequestExecutorBuilder"/> is <c>null</c>.
131+
/// The <paramref name="builder"/> is <c>null</c>.
122132
/// </exception>
123133
public static IRequestExecutorBuilder ExportSchemaOnStartup(
124134
this IRequestExecutorBuilder builder,
125-
string? schemaFileName = null)
135+
string? schemaFileName = null,
136+
bool skipIf = false)
126137
{
127138
ArgumentNullException.ThrowIfNull(builder);
128139

129-
return InitializeOnStartup(builder, new RequestExecutorInitializationOptions
130-
{
131-
KeepWarm = true,
132-
WriteSchemaFile = new SchemaFileInitializationOptions
133-
{
134-
Enable = true,
135-
FileName = schemaFileName
136-
}
137-
});
140+
schemaFileName ??= System.IO.Path.Combine(Environment.CurrentDirectory, "schema.graphqls");
141+
142+
return builder.AddWarmupTask(new SchemaFileExporterWarmupTask(schemaFileName), skipIf);
138143
}
139144
}

src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/HotChocolateAspNetCoreServiceCollectionExtensions.cs

Lines changed: 57 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using HotChocolate.AspNetCore.Instrumentation;
55
using HotChocolate.AspNetCore.ParameterExpressionBuilders;
66
using HotChocolate.AspNetCore.Parsers;
7+
using HotChocolate.AspNetCore.Warmup;
78
using HotChocolate.Execution.Configuration;
89
using HotChocolate.Internal;
910
using HotChocolate.Language;
@@ -19,53 +20,6 @@ namespace Microsoft.Extensions.DependencyInjection;
1920
/// </summary>
2021
public static partial class HotChocolateAspNetCoreServiceCollectionExtensions
2122
{
22-
private static IRequestExecutorBuilder AddGraphQLServerCore(
23-
this IRequestExecutorBuilder builder,
24-
int maxAllowedRequestSize = MaxAllowedRequestSize)
25-
{
26-
builder.ConfigureSchemaServices(s =>
27-
{
28-
s.TryAddSingleton<IHttpResponseFormatter>(
29-
sp => DefaultHttpResponseFormatter.Create(
30-
new HttpResponseFormatterOptions { HttpTransportVersion = HttpTransportVersion.Latest },
31-
sp.GetRequiredService<ITimeProvider>()));
32-
s.TryAddSingleton<IHttpRequestParser>(
33-
sp => new DefaultHttpRequestParser(
34-
sp.GetRequiredService<IDocumentCache>(),
35-
sp.GetRequiredService<IDocumentHashProvider>(),
36-
maxAllowedRequestSize,
37-
sp.GetRequiredService<ParserOptions>()));
38-
39-
s.TryAddSingleton<IServerDiagnosticEvents>(sp =>
40-
{
41-
var listeners = sp.GetServices<IServerDiagnosticEventListener>().ToArray();
42-
return listeners.Length switch
43-
{
44-
0 => new NoopServerDiagnosticEventListener(),
45-
1 => listeners[0],
46-
_ => new AggregateServerDiagnosticEventListener(listeners)
47-
};
48-
});
49-
});
50-
51-
if (!builder.Services.IsImplementationTypeRegistered<HttpContextParameterExpressionBuilder>())
52-
{
53-
builder.Services.AddSingleton<IParameterExpressionBuilder, HttpContextParameterExpressionBuilder>();
54-
}
55-
56-
if (!builder.Services.IsImplementationTypeRegistered<HttpRequestParameterExpressionBuilder>())
57-
{
58-
builder.Services.AddSingleton<IParameterExpressionBuilder, HttpRequestParameterExpressionBuilder>();
59-
}
60-
61-
if (!builder.Services.IsImplementationTypeRegistered<HttpResponseParameterExpressionBuilder>())
62-
{
63-
builder.Services.AddSingleton<IParameterExpressionBuilder, HttpResponseParameterExpressionBuilder>();
64-
}
65-
66-
return builder;
67-
}
68-
6923
/// <summary>
7024
/// Adds a GraphQL server configuration to the DI.
7125
/// </summary>
@@ -95,6 +49,7 @@ public static IRequestExecutorBuilder AddGraphQLServer(
9549
var builder = services
9650
.AddGraphQL(schemaName)
9751
.AddGraphQLServerCore(maxAllowedRequestSize)
52+
.AddStartupInitialization()
9853
.AddDefaultHttpRequestInterceptor()
9954
.AddSubscriptionServices();
10055

@@ -160,4 +115,59 @@ public static IRequestExecutorBuilder AddUploadType(
160115
builder.AddType<UploadType>();
161116
return builder;
162117
}
118+
119+
private static IRequestExecutorBuilder AddGraphQLServerCore(
120+
this IRequestExecutorBuilder builder,
121+
int maxAllowedRequestSize = MaxAllowedRequestSize)
122+
{
123+
builder.ConfigureSchemaServices(s =>
124+
{
125+
s.TryAddSingleton<IHttpResponseFormatter>(
126+
sp => DefaultHttpResponseFormatter.Create(
127+
new HttpResponseFormatterOptions { HttpTransportVersion = HttpTransportVersion.Latest },
128+
sp.GetRequiredService<ITimeProvider>()));
129+
s.TryAddSingleton<IHttpRequestParser>(
130+
sp => new DefaultHttpRequestParser(
131+
sp.GetRequiredService<IDocumentCache>(),
132+
sp.GetRequiredService<IDocumentHashProvider>(),
133+
maxAllowedRequestSize,
134+
sp.GetRequiredService<ParserOptions>()));
135+
136+
s.TryAddSingleton<IServerDiagnosticEvents>(sp =>
137+
{
138+
var listeners = sp.GetServices<IServerDiagnosticEventListener>().ToArray();
139+
return listeners.Length switch
140+
{
141+
0 => new NoopServerDiagnosticEventListener(),
142+
1 => listeners[0],
143+
_ => new AggregateServerDiagnosticEventListener(listeners)
144+
};
145+
});
146+
});
147+
148+
if (!builder.Services.IsImplementationTypeRegistered<HttpContextParameterExpressionBuilder>())
149+
{
150+
builder.Services.AddSingleton<IParameterExpressionBuilder, HttpContextParameterExpressionBuilder>();
151+
}
152+
153+
if (!builder.Services.IsImplementationTypeRegistered<HttpRequestParameterExpressionBuilder>())
154+
{
155+
builder.Services.AddSingleton<IParameterExpressionBuilder, HttpRequestParameterExpressionBuilder>();
156+
}
157+
158+
if (!builder.Services.IsImplementationTypeRegistered<HttpResponseParameterExpressionBuilder>())
159+
{
160+
builder.Services.AddSingleton<IParameterExpressionBuilder, HttpResponseParameterExpressionBuilder>();
161+
}
162+
163+
return builder;
164+
}
165+
166+
private static IRequestExecutorBuilder AddStartupInitialization(
167+
this IRequestExecutorBuilder builder)
168+
{
169+
builder.Services.AddHostedService<RequestExecutorWarmupService>();
170+
171+
return builder;
172+
}
163173
}

src/HotChocolate/AspNetCore/src/AspNetCore/Warmup/RequestExecutorInitializationOptions.cs

Lines changed: 0 additions & 23 deletions
This file was deleted.

0 commit comments

Comments
 (0)