Skip to content

Commit

Permalink
Schema Stitching Refinements (#2526)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelstaib committed Nov 2, 2020
1 parent 5a35105 commit 601190b
Show file tree
Hide file tree
Showing 68 changed files with 1,348 additions and 392 deletions.
@@ -0,0 +1,22 @@
using System;
using HotChocolate.AspNetCore.Utilities;
using HotChocolate.Execution.Configuration;

namespace Microsoft.Extensions.DependencyInjection
{
public static partial class HotChocolateAspNetCoreServiceCollectionExtensions
{
public static IRequestExecutorBuilder InitializeOnStartup(
this IRequestExecutorBuilder builder)
{
if (builder is null)
{
throw new ArgumentNullException(nameof(builder));
}

builder.Services.AddHostedService<ExecutorWarmupTask>();
builder.Services.AddSingleton(new WarmupSchema(builder.Name));
return builder;
}
}
}
@@ -1,6 +1,7 @@
using System;
using Microsoft.Extensions.DependencyInjection.Extensions;
using HotChocolate;
using HotChocolate.AspNetCore;
using HotChocolate.AspNetCore.Utilities;
using HotChocolate.Execution.Configuration;
using HotChocolate.Language;
Expand Down
@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using HotChocolate.Execution;
using Microsoft.Extensions.Hosting;

namespace HotChocolate.AspNetCore.Utilities
{
internal class ExecutorWarmupTask : BackgroundService
{
private readonly IRequestExecutorResolver _executorResolver;
private readonly HashSet<NameString> _schemaNames;

public ExecutorWarmupTask(
IRequestExecutorResolver executorResolver,
IEnumerable<WarmupSchema> schemas)
{
if (executorResolver is null)
{
throw new ArgumentNullException(nameof(executorResolver));
}

if (schemas is null)
{
throw new ArgumentNullException(nameof(schemas));
}

_executorResolver = executorResolver;
_schemaNames = new HashSet<NameString>(schemas.Select(t => t.SchemaName));
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
foreach (NameString schemaName in _schemaNames)
{
await _executorResolver
.GetRequestExecutorAsync(schemaName, stoppingToken)
.ConfigureAwait(false);
}
}
}
}
@@ -0,0 +1,12 @@
namespace HotChocolate.AspNetCore.Utilities
{
internal class WarmupSchema
{
public WarmupSchema(NameString schemaName)
{
SchemaName = schemaName;
}

public NameString SchemaName { get; }
}
}
@@ -1,38 +1,32 @@
using System;
using System.ComponentModel.Design;

namespace HotChocolate.Execution.Configuration
{
public sealed class NamedRequestExecutorFactoryOptions
: INamedRequestExecutorFactoryOptions
public sealed class ConfigureRequestExecutorSetup
: IConfigureRequestExecutorSetup
{
private readonly Action<RequestExecutorFactoryOptions> _configure;
private readonly Action<RequestExecutorSetup> _configure;

public NamedRequestExecutorFactoryOptions(
public ConfigureRequestExecutorSetup(
NameString schemaName,
Action<RequestExecutorFactoryOptions> configure)
Action<RequestExecutorSetup> configure)
{
SchemaName = schemaName.EnsureNotEmpty(nameof(schemaName));
_configure = configure ?? throw new ArgumentNullException(nameof(configure));
}

/*
public NamedRequestExecutorFactoryOptions(
public ConfigureRequestExecutorSetup(
NameString schemaName,
RequestExecutorFactoryOptions options)
RequestExecutorSetup options)
{
SchemaName = schemaName.EnsureNotEmpty(nameof(schemaName));
_configure = o =>
{
options.Pipeline
}
_configure = options.CopyTo;
}
*/

public NameString SchemaName { get; }

public void Configure(RequestExecutorFactoryOptions options)
public void Configure(RequestExecutorSetup options)
{
if (options is null)
{
Expand Down
@@ -1,5 +1,4 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
Expand All @@ -10,38 +9,43 @@ namespace HotChocolate.Execution.Configuration
{
internal sealed class DefaultRequestExecutorOptionsMonitor
: IRequestExecutorOptionsMonitor
, IDisposable
, IDisposable
{
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
private readonly IOptionsMonitor<RequestExecutorFactoryOptions> _optionsMonitor;
private readonly IOptionsMonitor<RequestExecutorSetup> _optionsMonitor;
private readonly IRequestExecutorOptionsProvider[] _optionsProviders;
private readonly ConcurrentDictionary<NameString, INamedRequestExecutorFactoryOptions>
_options = new ConcurrentDictionary<NameString, INamedRequestExecutorFactoryOptions>();
private readonly Dictionary<NameString, List<IConfigureRequestExecutorSetup>> _configs =
new Dictionary<NameString, List<IConfigureRequestExecutorSetup>>();
private readonly List<IDisposable> _disposables = new List<IDisposable>();
private readonly List<Action<RequestExecutorFactoryOptions, string>> _listeners =
new List<Action<RequestExecutorFactoryOptions, string>>();
private readonly List<Action<NameString>> _listeners = new List<Action<NameString>>();
private bool _initialized;
private bool _disposed;

public DefaultRequestExecutorOptionsMonitor(
IOptionsMonitor<RequestExecutorFactoryOptions> optionsMonitor,
IOptionsMonitor<RequestExecutorSetup> optionsMonitor,
IEnumerable<IRequestExecutorOptionsProvider> optionsProviders)
{
_optionsMonitor = optionsMonitor;
_optionsProviders = optionsProviders.ToArray();
}

public async ValueTask<RequestExecutorFactoryOptions> GetAsync(
public async ValueTask<RequestExecutorSetup> GetAsync(
NameString schemaName,
CancellationToken cancellationToken = default)
{
await InitializeAsync(cancellationToken).ConfigureAwait(false);

RequestExecutorFactoryOptions options = _optionsMonitor.Get(schemaName);
var options = new RequestExecutorSetup();
_optionsMonitor.Get(schemaName).CopyTo(options);

if (_options.TryGetValue(schemaName, out INamedRequestExecutorFactoryOptions? o))
if (_configs.TryGetValue(
schemaName,
out List<IConfigureRequestExecutorSetup>? configurations))
{
o.Configure(options);
foreach (IConfigureRequestExecutorSetup configuration in configurations)
{
configuration.Configure(options);
}
}

return options;
Expand All @@ -55,17 +59,27 @@ private async ValueTask InitializeAsync(CancellationToken cancellationToken)

if (!_initialized)
{
_configs.Clear();

foreach (IRequestExecutorOptionsProvider provider in _optionsProviders)
{
_disposables.Add(provider.OnChange(OnChange));

IEnumerable<INamedRequestExecutorFactoryOptions> allOptions =
IEnumerable<IConfigureRequestExecutorSetup> allConfigurations =
await provider.GetOptionsAsync(cancellationToken)
.ConfigureAwait(false);

foreach (NamedRequestExecutorFactoryOptions options in allOptions)
foreach (IConfigureRequestExecutorSetup configuration in allConfigurations)
{
_options[options.SchemaName] = options;
if (!_configs.TryGetValue(
configuration.SchemaName,
out List<IConfigureRequestExecutorSetup>? configurations))
{
configurations = new List<IConfigureRequestExecutorSetup>();
_configs.Add(configuration.SchemaName, configurations);
}

configurations.Add(configuration);
}
}

Expand All @@ -76,21 +90,18 @@ await provider.GetOptionsAsync(cancellationToken)
}
}

public IDisposable OnChange(Action<RequestExecutorFactoryOptions, string> listener) =>
public IDisposable OnChange(Action<NameString> listener) =>
new Session(this, listener);

private void OnChange(INamedRequestExecutorFactoryOptions changes)
private void OnChange(IConfigureRequestExecutorSetup changes)
{
_options[changes.SchemaName] = changes;

RequestExecutorFactoryOptions options = _optionsMonitor.Get(changes.SchemaName);
changes.Configure(options);
_initialized = false;

lock (_listeners)
{
foreach (Action<RequestExecutorFactoryOptions, string> listener in _listeners)
foreach (Action<NameString> listener in _listeners)
{
listener.Invoke(options, changes.SchemaName);
listener.Invoke(changes.SchemaName);
}
}
}
Expand All @@ -113,11 +124,11 @@ public void Dispose()
private class Session : IDisposable
{
private readonly DefaultRequestExecutorOptionsMonitor _monitor;
private readonly Action<RequestExecutorFactoryOptions, string> _listener;
private readonly Action<NameString> _listener;

public Session(
DefaultRequestExecutorOptionsMonitor monitor,
Action<RequestExecutorFactoryOptions, string> listener)
Action<NameString> listener)
{
lock (monitor._listeners)
{
Expand Down
Expand Up @@ -3,10 +3,10 @@
namespace HotChocolate.Execution.Configuration
{
/// <summary>
/// Represents something that configures the <see cref="RequestExecutorFactoryOptions"/>.
/// Represents something that configures the <see cref="RequestExecutorSetup"/>.
/// </summary>
public interface INamedRequestExecutorFactoryOptions
: IConfigureOptions<RequestExecutorFactoryOptions>
public interface IConfigureRequestExecutorSetup
: IConfigureOptions<RequestExecutorSetup>
{
/// <summary>
/// The schema name to which this instance provides configurations to.
Expand Down
Expand Up @@ -5,28 +5,28 @@
namespace HotChocolate.Execution.Configuration
{
/// <summary>
/// Used for notifications when <see cref="RequestExecutorFactoryOptions"/> instances change.
/// Used for notifications when <see cref="RequestExecutorSetup"/> instances change.
/// </summary>
public interface IRequestExecutorOptionsMonitor
{
/// <summary>
/// Returns a configured <see cref="RequestExecutorFactoryOptions"/>
/// Returns a configured <see cref="RequestExecutorSetup"/>
/// instance with the given name.
/// </summary>
ValueTask<RequestExecutorFactoryOptions> GetAsync(
ValueTask<RequestExecutorSetup> GetAsync(
NameString schemaName,
CancellationToken cancellationToken);

/// <summary>
/// Registers a listener to be called whenever a named
/// <see cref="RequestExecutorFactoryOptions"/> changes.
/// <see cref="RequestExecutorSetup"/> changes.
/// </summary>
/// <param name="listener">
/// The action to be invoked when <see cref="RequestExecutorFactoryOptions"/> has changed.
/// The action to be invoked when <see cref="RequestExecutorSetup"/> has changed.
/// </param>
/// <returns>
/// An <see cref="IDisposable"/> which should be disposed to stop listening for changes.
/// </returns>
IDisposable OnChange(Action<RequestExecutorFactoryOptions, string> listener);
IDisposable OnChange(Action<NameString> listener);
}
}
Expand Up @@ -19,19 +19,19 @@ public interface IRequestExecutorOptionsProvider
/// <returns>
/// Returns the configuration options of this provider.
/// </returns>
ValueTask<IEnumerable<INamedRequestExecutorFactoryOptions>> GetOptionsAsync(
ValueTask<IEnumerable<IConfigureRequestExecutorSetup>> GetOptionsAsync(
CancellationToken cancellationToken);

/// <summary>
/// Registers a listener to be called whenever a named
/// <see cref="RequestExecutorFactoryOptions"/> changes.
/// <see cref="RequestExecutorSetup"/> changes.
/// </summary>
/// <param name="listener">
/// The action to be invoked when <see cref="RequestExecutorFactoryOptions"/> has changed.
/// The action to be invoked when <see cref="RequestExecutorSetup"/> has changed.
/// </param>
/// <returns>
/// An <see cref="IDisposable"/> which should be disposed to stop listening for changes.
/// </returns>
IDisposable OnChange(Action<INamedRequestExecutorFactoryOptions> listener);
IDisposable OnChange(Action<IConfigureRequestExecutorSetup> listener);
}
}

This file was deleted.

0 comments on commit 601190b

Please sign in to comment.