Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public async Task StartAsync(CancellationToken cancellationToken)
{
var setup = optionsMonitor.Get(schemaName);

var requestOptions = FusionRequestExecutorManager.CreateRequestOptions(setup);
var requestOptions = FusionRequestExecutorManager.CreateOptions(setup);

if (!requestOptions.LazyInitialization)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ internal sealed class FusionGatewaySetup
{
public Func<IServiceProvider, IFusionConfigurationProvider>? DocumentProvider { get; set; }

public List<Action<FusionOptions>> OptionsModifiers { get; } = [];

public List<Action<FusionRequestOptions>> RequestOptionsModifiers { get; } = [];

public List<Action<FusionParserOptions>> ParserOptionsModifiers { get; } = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ namespace Microsoft.Extensions.DependencyInjection;

public static partial class CoreFusionGatewayBuilderExtensions
{
public static IFusionGatewayBuilder ModifyOptions(
this IFusionGatewayBuilder builder,
Action<FusionOptions> configure)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(configure);

return FusionSetupUtilities.Configure(
builder,
options => options.OptionsModifiers.Add(configure));
}

public static IFusionGatewayBuilder ModifyRequestOptions(
this IFusionGatewayBuilder builder,
Action<FusionRequestOptions> configure)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ internal static ErrorHandlingMode ErrorHandlingMode(
return errorHandlingMode;
}

return requestOptions.DefaultErrorHandlingMode;
return context.Schema.GetOptions().DefaultErrorHandlingMode;
}

internal static bool AllowErrorHandlingModeOverride(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ namespace HotChocolate.Execution;
/// </summary>
public static class FusionSchemaDefinitionExtensions
{
public static FusionOptions GetOptions(this ISchemaDefinition schema)
{
ArgumentNullException.ThrowIfNull(schema);

return schema.Features.GetRequired<FusionOptions>();
}

public static FusionRequestOptions GetRequestOptions(
this ISchemaDefinition schema)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
using HotChocolate.Caching.Memory;
using HotChocolate.Execution.Relay;
using HotChocolate.Language;
using HotChocolate.PersistedOperations;

namespace HotChocolate.Fusion.Execution;

public sealed class FusionOptions : ICloneable
{
private bool _isReadOnly;

/// <summary>
/// Gets or sets the time that the executor manager waits to dispose the schema services.
/// <c>30s</c> by default.
/// </summary>
public TimeSpan EvictionTimeout
{
get;
set
{
ExpectMutableOptions();

field = value;
}
} = TimeSpan.FromSeconds(30);

/// <summary>
/// Gets or sets the size of the operation execution plan cache.
/// <c>256</c> by default. <c>16</c> is the minimum.
/// </summary>
public int OperationExecutionPlanCacheSize
{
get;
set
{
ExpectMutableOptions();

field = value < 16
? 16
: value;
}
} = 256;

/// <summary>
/// Gets or sets the diagnostics for the operation execution plan cache.
/// </summary>
public CacheDiagnostics? OperationExecutionPlanCacheDiagnostics
{
get;
set
{
ExpectMutableOptions();

field = value;
}
}

/// <summary>
/// Gets or sets the size of the operation document cache.
/// <c>256</c> by default. <c>16</c> is the minimum.
/// </summary>
public int OperationDocumentCacheSize
{
get;
set
{
ExpectMutableOptions();

field = value < 16
? 16
: value;
}
} = 256;

/// <summary>
/// Gets or sets the default error handling mode.
/// <see cref="ErrorHandlingMode.Propagate"/> by default.
/// </summary>
public ErrorHandlingMode DefaultErrorHandlingMode
{
get;
set
{
ExpectMutableOptions();

field = value;
}
} = ErrorHandlingMode.Propagate;

/// <summary>
/// Gets or sets whether the request executor should be initialized lazily.
/// <c>false</c> by default.
/// </summary>
/// <remarks>
/// When set to <c>false</c> the creation of the schema and request executor, as well as
/// the load of the Fusion configuration, is deferred until the request executor
/// is first requested.
/// This can significantly slow down and block initial requests.
/// Therefore it is recommended to not use this option for production environments.
/// </remarks>
public bool LazyInitialization
{
get;
set
{
ExpectMutableOptions();

field = value;
}
}

/// <summary>
/// Specifies the format for Global Object Identifiers.
/// <see cref="NodeIdSerializerFormat.Base64"/> by default.
/// </summary>
public NodeIdSerializerFormat NodeIdSerializerFormat
{
get;
set
{
ExpectMutableOptions();

field = value;
}
} = NodeIdSerializerFormat.Base64;

/// <summary>
/// Clones the options into a new mutable instance.
/// </summary>
/// <returns>
/// A new mutable instance of <see cref="FusionOptions"/> with the same properties.
/// </returns>
public FusionOptions Clone()
{
return new FusionOptions
{
EvictionTimeout = EvictionTimeout,
OperationExecutionPlanCacheSize = OperationExecutionPlanCacheSize,
OperationExecutionPlanCacheDiagnostics = OperationExecutionPlanCacheDiagnostics,
OperationDocumentCacheSize = OperationDocumentCacheSize,
DefaultErrorHandlingMode = DefaultErrorHandlingMode,
LazyInitialization = LazyInitialization,
NodeIdSerializerFormat = NodeIdSerializerFormat
};
}

object ICloneable.Clone() => Clone();

internal void MakeReadOnly()
=> _isReadOnly = true;

private void ExpectMutableOptions()
{
if (_isReadOnly)
{
throw new InvalidOperationException("The options are read-only.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,7 @@ private async ValueTask EvictExecutorAsync(FusionRequestExecutor executor, Cance

private static async Task EvictRequestExecutorAsync(FusionRequestExecutor previousExecutor)
{
var evictionTimeout = previousExecutor.Schema.Features
.GetRequired<FusionRequestOptions>().EvictionTimeout;
var evictionTimeout = previousExecutor.Schema.GetOptions().EvictionTimeout;

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

var options = CreateOptions(setup);
var requestOptions = CreateRequestOptions(setup);
var parserOptions = CreateParserOptions(setup);
var clientConfigurations = CreateClientConfigurations(setup, configuration.Settings.Document);
var features = CreateSchemaFeatures(setup, requestOptions, parserOptions, clientConfigurations);
var schemaServices = CreateSchemaServices(setup, requestOptions);
var features = CreateSchemaFeatures(
setup,
options,
requestOptions,
parserOptions,
clientConfigurations);
var schemaServices = CreateSchemaServices(setup, options, requestOptions);

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

internal static FusionRequestOptions CreateRequestOptions(FusionGatewaySetup setup)
public static FusionOptions CreateOptions(FusionGatewaySetup setup)
{
var options = new FusionOptions();

foreach (var configure in setup.OptionsModifiers)
{
configure.Invoke(options);
}

options.MakeReadOnly();

return options;
}

private static FusionRequestOptions CreateRequestOptions(FusionGatewaySetup setup)
{
var options = new FusionRequestOptions();

Expand Down Expand Up @@ -269,12 +288,14 @@ private SourceSchemaClientConfigurations CreateClientConfigurations(

private FeatureCollection CreateSchemaFeatures(
FusionGatewaySetup setup,
FusionOptions options,
FusionRequestOptions requestOptions,
ParserOptions parserOptions,
SourceSchemaClientConfigurations clientConfigurations)
{
var features = new FeatureCollection();

features.Set(options);
features.Set(requestOptions);
features.Set(requestOptions.PersistedOperations);
features.Set(parserOptions);
Expand Down Expand Up @@ -304,11 +325,12 @@ private static Dictionary<string, ITypeResolverInterceptor> CreateTypeResolverIn

private ServiceProvider CreateSchemaServices(
FusionGatewaySetup setup,
FusionOptions options,
FusionRequestOptions requestOptions)
{
var schemaServices = new ServiceCollection();

AddCoreServices(schemaServices, requestOptions);
AddCoreServices(schemaServices, options, requestOptions);
AddOperationPlanner(schemaServices);
AddParserServices(schemaServices);
AddDocumentValidator(setup, schemaServices);
Expand All @@ -322,7 +344,10 @@ private ServiceProvider CreateSchemaServices(
return schemaServices.BuildServiceProvider();
}

private void AddCoreServices(IServiceCollection services, FusionRequestOptions requestOptions)
private void AddCoreServices(
IServiceCollection services,
FusionOptions options,
FusionRequestOptions requestOptions)
{
services.AddSingleton<IRootServiceProviderAccessor>(
new RootServiceProviderAccessor(_applicationServices));
Expand All @@ -333,7 +358,7 @@ private void AddCoreServices(IServiceCollection services, FusionRequestOptions r
services.AddSingleton(static sp => sp.GetRequiredService<ISchemaDefinition>().GetRequestOptions());
services.TryAddSingleton<INodeIdParser>(
static sp => new DefaultNodeIdParser(
sp.GetRequiredService<FusionRequestOptions>().NodeIdSerializerFormat));
sp.GetRequiredService<FusionOptions>().NodeIdSerializerFormat));
services.AddSingleton<IErrorHandler>(static sp => new DefaultErrorHandler(sp.GetServices<IErrorFilter>()));

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

services.AddSingleton(options);
services.AddSingleton(requestOptions);
services.AddSingleton(requestOptions.PersistedOperations);

Expand All @@ -366,7 +392,7 @@ private static void AddOperationPlanner(IServiceCollection services)
services.AddSingleton(
static sp =>
{
var options = sp.GetRequiredService<ISchemaDefinition>().GetRequestOptions();
var options = sp.GetRequiredService<ISchemaDefinition>().GetOptions();
return new Cache<OperationPlan>(
options.OperationExecutionPlanCacheSize,
options.OperationExecutionPlanCacheDiagnostics);
Expand All @@ -390,7 +416,7 @@ private static void AddParserServices(IServiceCollection services)
services.AddSingleton<IDocumentCache>(
static sp =>
{
var options = sp.GetRequiredService<ISchemaDefinition>().GetRequestOptions();
var options = sp.GetRequiredService<ISchemaDefinition>().GetOptions();
return new DefaultDocumentCache(options.OperationDocumentCacheSize);
});
}
Expand Down
Loading
Loading