Skip to content

Commit

Permalink
Reworked the fusion execution state management. (#6325)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelstaib authored Jul 12, 2023
1 parent 2ea2bed commit 77913da
Show file tree
Hide file tree
Showing 14 changed files with 189 additions and 92 deletions.
4 changes: 2 additions & 2 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
rulers = 100
rulers = 120

[*.md]
trim_trailing_whitespace = false
Expand Down Expand Up @@ -100,4 +100,4 @@ csharp_preserve_single_line_blocks = true
dotnet_style_require_accessibility_modifiers = always

# Public API
dotnet_diagnostic.RS0016.severity = warning
dotnet_diagnostic.RS0016.severity = warning
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"**/*.code-search": true,
"**/PublicAPI*.txt": true
},
"editor.rulers": [100],
"editor.rulers": [120],
"files.trimTrailingWhitespace": true,
"files.associations": {
"*.*proj": "xml",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,39 @@ namespace HotChocolate.ApolloFederation;
internal sealed class FederationTypeInterceptor : TypeInterceptor
{
private static readonly object _empty = new();

private static readonly MethodInfo _matches =
typeof(ReferenceResolverHelper)
.GetMethod(
nameof(ReferenceResolverHelper.Matches),
BindingFlags.Static | BindingFlags.Public)!;

private static readonly MethodInfo _execute =
typeof(ReferenceResolverHelper)
.GetMethod(
nameof(ReferenceResolverHelper.ExecuteAsync),
BindingFlags.Static | BindingFlags.Public)!;

private static readonly MethodInfo _invalid =
typeof(ReferenceResolverHelper)
.GetMethod(
nameof(ReferenceResolverHelper.Invalid),
BindingFlags.Static | BindingFlags.Public)!;

private readonly List<ObjectType> _entityTypes = new();
private IDescriptorContext _context = default!;
private ITypeInspector _typeInspector = default!;

internal override void InitializeContext(
IDescriptorContext context,
TypeInitializer typeInitializer,
TypeRegistry typeRegistry,
TypeLookup typeLookup,
TypeReferenceResolver typeReferenceResolver)
{
_context = context;
_typeInspector = context.TypeInspector;
}

public override void OnAfterInitialize(
ITypeDiscoveryContext discoveryContext,
Expand All @@ -46,8 +63,7 @@ public override void OnAfterInitialize(
{
ApplyMethodLevelReferenceResolvers(
objectType,
objectTypeDefinition,
discoveryContext);
objectTypeDefinition);

AddToUnionIfHasTypeLevelKeyDirective(
objectType,
Expand Down Expand Up @@ -147,49 +163,47 @@ private void AddServiceTypeToQueryType(
definition is ObjectTypeDefinition objectTypeDefinition)
{
var serviceFieldDescriptor = ObjectFieldDescriptor.New(
completionContext.DescriptorContext,
_context,
WellKnownFieldNames.Service);
serviceFieldDescriptor
.Type<NonNullType<ServiceType>>()
.Resolve(_empty);
objectTypeDefinition.Fields.Add(serviceFieldDescriptor.CreateDefinition());

var entitiesFieldDescriptor = ObjectFieldDescriptor.New(
completionContext.DescriptorContext,
_context,
WellKnownFieldNames.Entities);
entitiesFieldDescriptor
.Type<NonNullType<ListType<EntityType>>>()
.Argument(
WellKnownArgumentNames.Representations,
descriptor => descriptor.Type<NonNullType<ListType<NonNullType<AnyType>>>>())
.Resolve(c => EntitiesResolver.ResolveAsync(
c.Schema,
c.ArgumentValue<IReadOnlyList<Representation>>(
WellKnownArgumentNames.Representations),
c
));
.Resolve(
c => EntitiesResolver.ResolveAsync(
c.Schema,
c.ArgumentValue<IReadOnlyList<Representation>>(
WellKnownArgumentNames.Representations),
c
));
objectTypeDefinition.Fields.Add(entitiesFieldDescriptor.CreateDefinition());
}
}

private void ApplyMethodLevelReferenceResolvers(
ObjectType objectType,
ObjectTypeDefinition objectTypeDefinition,
ITypeDiscoveryContext discoveryContext)
ObjectTypeDefinition objectTypeDefinition)
{
if (objectType.RuntimeType != typeof(object))
{
var descriptorContext = discoveryContext.DescriptorContext;
var typeInspector = discoveryContext.TypeInspector;
var descriptor = ObjectTypeDescriptor.From(descriptorContext, objectTypeDefinition);
var descriptor = ObjectTypeDescriptor.From(_context, objectTypeDefinition);

foreach (var possibleReferenceResolver in
objectType.RuntimeType.GetMethods(BindingFlags.Static | BindingFlags.Public))
{
if (possibleReferenceResolver.IsDefined(typeof(ReferenceResolverAttribute)))
{
typeInspector.ApplyAttributes(
descriptorContext,
_typeInspector.ApplyAttributes(
_context,
descriptor,
possibleReferenceResolver);
}
Expand All @@ -204,7 +218,7 @@ private void AddToUnionIfHasTypeLevelKeyDirective(
ObjectTypeDefinition objectTypeDefinition)
{
if (objectTypeDefinition.Directives.Any(
d => d.Value is DirectiveNode { Name.Value: WellKnownTypeNames.Key }) ||
d => d.Value is DirectiveNode { Name.Value: WellKnownTypeNames.Key }) ||
objectTypeDefinition.Fields.Any(f => f.ContextData.ContainsKey(WellKnownTypeNames.Key)))
{
_entityTypes.Add(objectType);
Expand Down Expand Up @@ -284,4 +298,4 @@ private static void AddKeyDirective(
objectTypeDefinition.Directives.Add(
new DirectiveDefinition(directiveNode));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ private sealed class FileConfigurationSession : IDisposable

public FileConfigurationSession(IObserver<GatewayConfiguration> observer, string fileName)
{
var fullPath = Path.GetFullPath(fileName);
var directory = Path.GetDirectoryName(fullPath);

_observer = observer;
_fileName = fileName;
_fileName = fullPath;

BeginLoadConfig();

var fullPath = Path.GetFullPath(fileName);
var directory = Path.GetDirectoryName(fullPath);

if (directory is null)
{
throw new FileNotFoundException(
Expand Down
94 changes: 72 additions & 22 deletions src/HotChocolate/Fusion/src/Core/Execution/ExecutionState.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using HotChocolate.Execution.Processing;
using static HotChocolate.Fusion.FusionResources;

namespace HotChocolate.Fusion.Execution;

internal sealed class ExecutionState
{
private readonly Dictionary<ISelectionSet, List<WorkItem>> _map = new();
private readonly Dictionary<ISelectionSet, List<SelectionSetState>> _map = new();
private readonly HashSet<ISelectionSet> _immutable = new();

public bool ContainsState(ISelectionSet selectionSet)
Expand Down Expand Up @@ -61,7 +65,7 @@ public bool ContainsState(ISelectionSet[] selectionSets)

public bool TryGetState(
ISelectionSet selectionSet,
[NotNullWhen(true)] out IReadOnlyList<WorkItem>? values)
[NotNullWhen(true)] out IReadOnlyList<SelectionSetState>? values)
{
var taken = false;
Monitor.Enter(_map, ref taken);
Expand Down Expand Up @@ -97,24 +101,33 @@ public bool TryGetState(
}
}

public void RegisterState(WorkItem value)
public void TryRegisterState(
ISelectionSet selectionSet,
ObjectResult result,
IReadOnlyList<string> exportKeys,
SelectionData parentData)
{
var taken = false;
List<WorkItem>? values;
List<SelectionSetState>? states;
SelectionSetState? state;
Monitor.Enter(_map, ref taken);

try
{
if (_immutable.Contains(value.SelectionSet))
if (_immutable.Contains(selectionSet))
{
throw new InvalidOperationException(
$"The state for the selection set `{value.SelectionSet.Id}` is immutable.");
return;
}

if (!_map.TryGetValue(value.SelectionSet, out values))
state = new SelectionSetState(selectionSet, result, exportKeys)
{
var temp = new List<WorkItem> { value };
_map.Add(value.SelectionSet, temp);
SelectionSetData = { [0] = parentData }
};

if (!_map.TryGetValue(state.SelectionSet, out states))
{
var temp = new List<SelectionSetState> { state };
_map.Add(state.SelectionSet, temp);
}
}
finally
Expand All @@ -125,23 +138,60 @@ public void RegisterState(WorkItem value)
}
}

if (values is not null)
AddState(states, state);
}

public void RegisterState(SelectionSetState state)
{
var taken = false;
List<SelectionSetState>? states;
Monitor.Enter(_map, ref taken);

try
{
taken = false;
Monitor.Enter(values, ref taken);
if (_immutable.Contains(state.SelectionSet))
{
throw new InvalidOperationException(
string.Format(ExecutionState_RegisterState_StateImmutable, state.SelectionSet.Id));
}

try
if (!_map.TryGetValue(state.SelectionSet, out states))
{
values.Add(value);
var temp = new List<SelectionSetState> { state };
_map.Add(state.SelectionSet, temp);
}
finally
}
finally
{
if (taken)
{
if (taken)
{
Monitor.Exit(values);
}
Monitor.Exit(_map);
}
}

AddState(states, state);
}
}

private static void AddState(List<SelectionSetState>? states, SelectionSetState state)
{
if (states is null)
{
return;
}

var taken = false;
Monitor.Enter(states, ref taken);

try
{
states.Add(state);
}
finally
{
if (taken)
{
Monitor.Exit(states);
}
}
}
}
Loading

0 comments on commit 77913da

Please sign in to comment.