Skip to content

Commit 0f64674

Browse files
[Fusion] Split options and request options apart (#8755)
1 parent dccdd58 commit 0f64674

File tree

9 files changed

+223
-139
lines changed

9 files changed

+223
-139
lines changed

src/HotChocolate/Fusion-vnext/src/Fusion.AspNetCore/RequestExecutorWarmupService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public async Task StartAsync(CancellationToken cancellationToken)
1818
{
1919
var setup = optionsMonitor.Get(schemaName);
2020

21-
var requestOptions = FusionRequestExecutorManager.CreateRequestOptions(setup);
21+
var requestOptions = FusionRequestExecutorManager.CreateOptions(setup);
2222

2323
if (!requestOptions.LazyInitialization)
2424
{

src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Configuration/FusionGatewaySetup.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ internal sealed class FusionGatewaySetup
1111
{
1212
public Func<IServiceProvider, IFusionConfigurationProvider>? DocumentProvider { get; set; }
1313

14+
public List<Action<FusionOptions>> OptionsModifiers { get; } = [];
15+
1416
public List<Action<FusionRequestOptions>> RequestOptionsModifiers { get; } = [];
1517

1618
public List<Action<FusionParserOptions>> ParserOptionsModifiers { get; } = [];

src/HotChocolate/Fusion-vnext/src/Fusion.Execution/DependencyInjection/CoreFusionGatewayBuilderExtensions.Options.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@ namespace Microsoft.Extensions.DependencyInjection;
55

66
public static partial class CoreFusionGatewayBuilderExtensions
77
{
8+
public static IFusionGatewayBuilder ModifyOptions(
9+
this IFusionGatewayBuilder builder,
10+
Action<FusionOptions> configure)
11+
{
12+
ArgumentNullException.ThrowIfNull(builder);
13+
ArgumentNullException.ThrowIfNull(configure);
14+
15+
return FusionSetupUtilities.Configure(
16+
builder,
17+
options => options.OptionsModifiers.Add(configure));
18+
}
19+
820
public static IFusionGatewayBuilder ModifyRequestOptions(
921
this IFusionGatewayBuilder builder,
1022
Action<FusionRequestOptions> configure)

src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Extensions/FusionRequestContextExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ internal static ErrorHandlingMode ErrorHandlingMode(
125125
return errorHandlingMode;
126126
}
127127

128-
return requestOptions.DefaultErrorHandlingMode;
128+
return context.Schema.GetOptions().DefaultErrorHandlingMode;
129129
}
130130

131131
internal static bool AllowErrorHandlingModeOverride(

src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Extensions/FusionSchemaDefinitionExtensions.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ namespace HotChocolate.Execution;
1212
/// </summary>
1313
public static class FusionSchemaDefinitionExtensions
1414
{
15+
public static FusionOptions GetOptions(this ISchemaDefinition schema)
16+
{
17+
ArgumentNullException.ThrowIfNull(schema);
18+
19+
return schema.Features.GetRequired<FusionOptions>();
20+
}
21+
1522
public static FusionRequestOptions GetRequestOptions(
1623
this ISchemaDefinition schema)
1724
{
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
using HotChocolate.Caching.Memory;
2+
using HotChocolate.Execution.Relay;
3+
using HotChocolate.Language;
4+
using HotChocolate.PersistedOperations;
5+
6+
namespace HotChocolate.Fusion.Execution;
7+
8+
public sealed class FusionOptions : ICloneable
9+
{
10+
private bool _isReadOnly;
11+
12+
/// <summary>
13+
/// Gets or sets the time that the executor manager waits to dispose the schema services.
14+
/// <c>30s</c> by default.
15+
/// </summary>
16+
public TimeSpan EvictionTimeout
17+
{
18+
get;
19+
set
20+
{
21+
ExpectMutableOptions();
22+
23+
field = value;
24+
}
25+
} = TimeSpan.FromSeconds(30);
26+
27+
/// <summary>
28+
/// Gets or sets the size of the operation execution plan cache.
29+
/// <c>256</c> by default. <c>16</c> is the minimum.
30+
/// </summary>
31+
public int OperationExecutionPlanCacheSize
32+
{
33+
get;
34+
set
35+
{
36+
ExpectMutableOptions();
37+
38+
field = value < 16
39+
? 16
40+
: value;
41+
}
42+
} = 256;
43+
44+
/// <summary>
45+
/// Gets or sets the diagnostics for the operation execution plan cache.
46+
/// </summary>
47+
public CacheDiagnostics? OperationExecutionPlanCacheDiagnostics
48+
{
49+
get;
50+
set
51+
{
52+
ExpectMutableOptions();
53+
54+
field = value;
55+
}
56+
}
57+
58+
/// <summary>
59+
/// Gets or sets the size of the operation document cache.
60+
/// <c>256</c> by default. <c>16</c> is the minimum.
61+
/// </summary>
62+
public int OperationDocumentCacheSize
63+
{
64+
get;
65+
set
66+
{
67+
ExpectMutableOptions();
68+
69+
field = value < 16
70+
? 16
71+
: value;
72+
}
73+
} = 256;
74+
75+
/// <summary>
76+
/// Gets or sets the default error handling mode.
77+
/// <see cref="ErrorHandlingMode.Propagate"/> by default.
78+
/// </summary>
79+
public ErrorHandlingMode DefaultErrorHandlingMode
80+
{
81+
get;
82+
set
83+
{
84+
ExpectMutableOptions();
85+
86+
field = value;
87+
}
88+
} = ErrorHandlingMode.Propagate;
89+
90+
/// <summary>
91+
/// Gets or sets whether the request executor should be initialized lazily.
92+
/// <c>false</c> by default.
93+
/// </summary>
94+
/// <remarks>
95+
/// When set to <c>false</c> the creation of the schema and request executor, as well as
96+
/// the load of the Fusion configuration, is deferred until the request executor
97+
/// is first requested.
98+
/// This can significantly slow down and block initial requests.
99+
/// Therefore it is recommended to not use this option for production environments.
100+
/// </remarks>
101+
public bool LazyInitialization
102+
{
103+
get;
104+
set
105+
{
106+
ExpectMutableOptions();
107+
108+
field = value;
109+
}
110+
}
111+
112+
/// <summary>
113+
/// Specifies the format for Global Object Identifiers.
114+
/// <see cref="NodeIdSerializerFormat.Base64"/> by default.
115+
/// </summary>
116+
public NodeIdSerializerFormat NodeIdSerializerFormat
117+
{
118+
get;
119+
set
120+
{
121+
ExpectMutableOptions();
122+
123+
field = value;
124+
}
125+
} = NodeIdSerializerFormat.Base64;
126+
127+
/// <summary>
128+
/// Clones the options into a new mutable instance.
129+
/// </summary>
130+
/// <returns>
131+
/// A new mutable instance of <see cref="FusionOptions"/> with the same properties.
132+
/// </returns>
133+
public FusionOptions Clone()
134+
{
135+
return new FusionOptions
136+
{
137+
EvictionTimeout = EvictionTimeout,
138+
OperationExecutionPlanCacheSize = OperationExecutionPlanCacheSize,
139+
OperationExecutionPlanCacheDiagnostics = OperationExecutionPlanCacheDiagnostics,
140+
OperationDocumentCacheSize = OperationDocumentCacheSize,
141+
DefaultErrorHandlingMode = DefaultErrorHandlingMode,
142+
LazyInitialization = LazyInitialization,
143+
NodeIdSerializerFormat = NodeIdSerializerFormat
144+
};
145+
}
146+
147+
object ICloneable.Clone() => Clone();
148+
149+
internal void MakeReadOnly()
150+
=> _isReadOnly = true;
151+
152+
private void ExpectMutableOptions()
153+
{
154+
if (_isReadOnly)
155+
{
156+
throw new InvalidOperationException("The options are read-only.");
157+
}
158+
}
159+
}

src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/FusionRequestExecutorManager.cs

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,7 @@ private async ValueTask EvictExecutorAsync(FusionRequestExecutor executor, Cance
123123

124124
private static async Task EvictRequestExecutorAsync(FusionRequestExecutor previousExecutor)
125125
{
126-
var evictionTimeout = previousExecutor.Schema.Features
127-
.GetRequired<FusionRequestOptions>().EvictionTimeout;
126+
var evictionTimeout = previousExecutor.Schema.GetOptions().EvictionTimeout;
128127

129128
// we will give the request executor some grace period to finish all requests
130129
// in the pipeline.
@@ -159,11 +158,17 @@ private FusionRequestExecutor CreateRequestExecutor(
159158
{
160159
var setup = _optionsMonitor.Get(schemaName);
161160

161+
var options = CreateOptions(setup);
162162
var requestOptions = CreateRequestOptions(setup);
163163
var parserOptions = CreateParserOptions(setup);
164164
var clientConfigurations = CreateClientConfigurations(setup, configuration.Settings.Document);
165-
var features = CreateSchemaFeatures(setup, requestOptions, parserOptions, clientConfigurations);
166-
var schemaServices = CreateSchemaServices(setup, requestOptions);
165+
var features = CreateSchemaFeatures(
166+
setup,
167+
options,
168+
requestOptions,
169+
parserOptions,
170+
clientConfigurations);
171+
var schemaServices = CreateSchemaServices(setup, options, requestOptions);
167172

168173
var schema = CreateSchema(schemaName, configuration.Schema, schemaServices, features);
169174
var pipeline = CreatePipeline(setup, schema, schemaServices, requestOptions);
@@ -201,7 +206,21 @@ private async Task WarmupExecutorAsync(IRequestExecutor executor, CancellationTo
201206
return (await documentPromise.Task.ConfigureAwait(false), documentProvider);
202207
}
203208

204-
internal static FusionRequestOptions CreateRequestOptions(FusionGatewaySetup setup)
209+
public static FusionOptions CreateOptions(FusionGatewaySetup setup)
210+
{
211+
var options = new FusionOptions();
212+
213+
foreach (var configure in setup.OptionsModifiers)
214+
{
215+
configure.Invoke(options);
216+
}
217+
218+
options.MakeReadOnly();
219+
220+
return options;
221+
}
222+
223+
private static FusionRequestOptions CreateRequestOptions(FusionGatewaySetup setup)
205224
{
206225
var options = new FusionRequestOptions();
207226

@@ -269,12 +288,14 @@ private SourceSchemaClientConfigurations CreateClientConfigurations(
269288

270289
private FeatureCollection CreateSchemaFeatures(
271290
FusionGatewaySetup setup,
291+
FusionOptions options,
272292
FusionRequestOptions requestOptions,
273293
ParserOptions parserOptions,
274294
SourceSchemaClientConfigurations clientConfigurations)
275295
{
276296
var features = new FeatureCollection();
277297

298+
features.Set(options);
278299
features.Set(requestOptions);
279300
features.Set(requestOptions.PersistedOperations);
280301
features.Set(parserOptions);
@@ -304,11 +325,12 @@ private static Dictionary<string, ITypeResolverInterceptor> CreateTypeResolverIn
304325

305326
private ServiceProvider CreateSchemaServices(
306327
FusionGatewaySetup setup,
328+
FusionOptions options,
307329
FusionRequestOptions requestOptions)
308330
{
309331
var schemaServices = new ServiceCollection();
310332

311-
AddCoreServices(schemaServices, requestOptions);
333+
AddCoreServices(schemaServices, options, requestOptions);
312334
AddOperationPlanner(schemaServices);
313335
AddParserServices(schemaServices);
314336
AddDocumentValidator(setup, schemaServices);
@@ -322,7 +344,10 @@ private ServiceProvider CreateSchemaServices(
322344
return schemaServices.BuildServiceProvider();
323345
}
324346

325-
private void AddCoreServices(IServiceCollection services, FusionRequestOptions requestOptions)
347+
private void AddCoreServices(
348+
IServiceCollection services,
349+
FusionOptions options,
350+
FusionRequestOptions requestOptions)
326351
{
327352
services.AddSingleton<IRootServiceProviderAccessor>(
328353
new RootServiceProviderAccessor(_applicationServices));
@@ -333,7 +358,7 @@ private void AddCoreServices(IServiceCollection services, FusionRequestOptions r
333358
services.AddSingleton(static sp => sp.GetRequiredService<ISchemaDefinition>().GetRequestOptions());
334359
services.TryAddSingleton<INodeIdParser>(
335360
static sp => new DefaultNodeIdParser(
336-
sp.GetRequiredService<FusionRequestOptions>().NodeIdSerializerFormat));
361+
sp.GetRequiredService<FusionOptions>().NodeIdSerializerFormat));
337362
services.AddSingleton<IErrorHandler>(static sp => new DefaultErrorHandler(sp.GetServices<IErrorFilter>()));
338363

339364
if (requestOptions.IncludeExceptionDetails)
@@ -345,6 +370,7 @@ private void AddCoreServices(IServiceCollection services, FusionRequestOptions r
345370
services.AddSingleton(static sp => sp.GetRequiredService<SchemaDefinitionAccessor>().Schema);
346371
services.AddSingleton<ISchemaDefinition>(static sp => sp.GetRequiredService<FusionSchemaDefinition>());
347372

373+
services.AddSingleton(options);
348374
services.AddSingleton(requestOptions);
349375
services.AddSingleton(requestOptions.PersistedOperations);
350376

@@ -366,7 +392,7 @@ private static void AddOperationPlanner(IServiceCollection services)
366392
services.AddSingleton(
367393
static sp =>
368394
{
369-
var options = sp.GetRequiredService<ISchemaDefinition>().GetRequestOptions();
395+
var options = sp.GetRequiredService<ISchemaDefinition>().GetOptions();
370396
return new Cache<OperationPlan>(
371397
options.OperationExecutionPlanCacheSize,
372398
options.OperationExecutionPlanCacheDiagnostics);
@@ -390,7 +416,7 @@ private static void AddParserServices(IServiceCollection services)
390416
services.AddSingleton<IDocumentCache>(
391417
static sp =>
392418
{
393-
var options = sp.GetRequiredService<ISchemaDefinition>().GetRequestOptions();
419+
var options = sp.GetRequiredService<ISchemaDefinition>().GetOptions();
394420
return new DefaultDocumentCache(options.OperationDocumentCacheSize);
395421
});
396422
}

0 commit comments

Comments
 (0)