Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
d852e0f
Add wide condition mask baseline
michaelstaib Aug 27, 2026
6a1c9d9
Add condition flag carrier
michaelstaib Aug 27, 2026
f7d388f
Flatten selection include masks
michaelstaib Aug 27, 2026
52216d2
Unify field collection
michaelstaib Aug 27, 2026
9829bf8
Adopt Fusion condition flags
michaelstaib Aug 27, 2026
a2fbf19
Expose condition flag accessors
michaelstaib Aug 27, 2026
1808ff7
Add wide projection selector cache keys
michaelstaib Aug 27, 2026
36ebdda
Share projection selector cache capacity
michaelstaib Aug 27, 2026
46d494b
Flatten Fusion selection include masks
michaelstaib Aug 27, 2026
1dc3a16
Unify Fusion field collection
michaelstaib Aug 27, 2026
80a03db
Migrate Data projections to condition flags
michaelstaib Aug 27, 2026
af8eb2d
Register cache diagnostics in schema services
michaelstaib Aug 27, 2026
c02dbac
Deprecate raw condition APIs
michaelstaib Aug 27, 2026
2639ad5
Update condition flag snapshots
michaelstaib Aug 27, 2026
b15cab3
Use condition flags for data selection
michaelstaib Aug 27, 2026
2b52431
Migrate conditional selector flags
michaelstaib Aug 27, 2026
15dd393
Deprecate Fusion raw condition masks
michaelstaib Aug 28, 2026
b594a88
Use condition flags in Fusion benchmark
michaelstaib Aug 30, 2026
b611ac8
Document condition flag migration
michaelstaib Aug 30, 2026
51dc19e
Fix condition limit migration example
michaelstaib Aug 30, 2026
ed7f09c
Fix Fusion gateway migration snippet
michaelstaib Aug 30, 2026
356e6dd
Add narrow condition benchmarks
michaelstaib Aug 27, 2026
163f5e6
Correct narrow condition benchmark harness
michaelstaib Aug 30, 2026
8860b31
Stabilize narrow condition benchmarks
michaelstaib Aug 30, 2026
41ce014
Optimize narrow condition carrier checks
michaelstaib Aug 30, 2026
7e4e1ae
Stabilize wide defer execution test
michaelstaib Aug 30, 2026
3773acf
Remove public condition word surfaces
michaelstaib Aug 30, 2026
4a33dcc
Harden raw projection flag APIs
michaelstaib Aug 30, 2026
1ab6757
Add 16.7 condition migration guides
michaelstaib Aug 30, 2026
38a16d6
Correct wide condition migration guidance
michaelstaib Aug 30, 2026
e7c4f26
Merge remote-tracking branch 'origin/main' into mst/wide-condition-masks
michaelstaib Aug 31, 2026
d276e94
Use shared cache for projection selectors
michaelstaib Aug 31, 2026
23c7cc3
Remove obsolete selector bridges
michaelstaib Aug 31, 2026
20a653f
polish
michaelstaib Aug 31, 2026
2713687
Require condition collection limits
michaelstaib Aug 31, 2026
1002187
polish
michaelstaib Aug 31, 2026
def30f4
Use condition flags for field selection paths
michaelstaib Aug 31, 2026
6b9e7ea
Move condition defaults to request options
michaelstaib Aug 31, 2026
2b2b939
polish
michaelstaib Aug 31, 2026
71d2e39
polish
michaelstaib Aug 31, 2026
e3fd3b0
Skip empty wide include masks
michaelstaib Aug 31, 2026
8a8b374
polish
michaelstaib Aug 31, 2026
dea7a6b
Remove FetchResultStore ulong Initialize overload
michaelstaib Aug 31, 2026
d8bd8bc
polish
michaelstaib Aug 31, 2026
cfbfcae
Merge branch 'main' into mst/wide-condition-masks
michaelstaib Sep 1, 2026
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 @@ -16,6 +16,7 @@

<ItemGroup>
<ProjectReference Include="..\..\src\Execution.Abstractions\HotChocolate.Execution.Abstractions.csproj" />
<ProjectReference Include="..\..\src\Types\HotChocolate.Types.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
using System.Collections.Generic;
using System.Text;
using System.Threading;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Toolchains.InProcess.Emit;
using HotChocolate;
using HotChocolate.Execution.Instrumentation;
using HotChocolate.Execution;
using HotChocolate.Execution.Processing;
using HotChocolate.Language;
using HotChocolate.Types;
using Microsoft.Extensions.DependencyInjection;

namespace HotChocolate.Execution.Abstractions.Benchmarks;

[MemoryDiagnoser]
[Config(typeof(BenchmarkConfig))]
public class NarrowConditionCachedExecutionBenchmark
{
private const int ConditionCount = 48;

private static readonly string s_documentText = CreateDocument();
private static readonly string s_expectedResult = CreateExpectedResult();
private static readonly IReadOnlyDictionary<string, object?> s_variables = CreateVariables();
private readonly CacheDiagnosticListener _diagnosticListener = new();

private IRequestExecutor _executor = null!;
private IOperationRequest _request = null!;

private sealed class BenchmarkConfig : ManualConfig
{
public BenchmarkConfig()
{
AddJob(
Job.Default
.WithWarmupCount(3)
.WithIterationCount(10)
.WithInvocationCount(1024)
.WithUnrollFactor(1)
.WithToolchain(InProcessEmitToolchain.Instance));
}
}

[GlobalSetup]
public async Task SetupAsync()
{
_executor = await new ServiceCollection()
.AddGraphQL()
.AddDiagnosticEventListener(_ => _diagnosticListener)
.AddQueryType(
descriptor => descriptor
.Name("Query")
.Field("value")
.Type<NonNullType<StringType>>()
.Resolve("value"))
.BuildRequestExecutorAsync();
_request = OperationRequestBuilder.New()
.SetDocument(s_documentText)
.SetVariableValues(s_variables)
.Build();

await VerifyCachedExecutionAsync();
}

[Benchmark(Baseline = true)]
public Task<ExecutionResultKind> Execute_Cached_Narrow()
=> ExecuteAsync();

[Benchmark]
public Task<ExecutionResultKind> Execute_Cached_Narrow_BaselineCopy()
=> ExecuteAsync();

private async Task VerifyCachedExecutionAsync()
{
_diagnosticListener.Reset();
await VerifyExpectedResultAsync();

if (_diagnosticListener.RetrievedOperationCount != 0)
{
throw new InvalidOperationException("The first narrow request unexpectedly retrieved a compiled operation.");
}

await VerifyExpectedResultAsync();

if (_diagnosticListener.RetrievedOperationCount != 1)
{
throw new InvalidOperationException("The second narrow request did not retrieve the compiled operation.");
}
}

private async Task VerifyExpectedResultAsync()
{
await using var result = await _executor.ExecuteAsync(_request);

if (result is not OperationResult operationResult
|| operationResult.Errors.Count != 0
|| !string.Equals(
result.ToJson(withIndentations: false),
s_expectedResult,
StringComparison.Ordinal))
{
throw new InvalidOperationException("The narrow request did not return the expected 48-field payload.");
}
}
private async Task<ExecutionResultKind> ExecuteAsync()
{
await using var result = await _executor.ExecuteAsync(_request);
return result.Kind;
}

private static IReadOnlyDictionary<string, object?> CreateVariables()
{
var variables = new Dictionary<string, object?>(ConditionCount);

for (var i = 0; i < ConditionCount; i++)
{
variables.Add($"v{i}", true);
}

return variables;
}

private static string CreateDocument()
{
var builder = new StringBuilder("query(");

for (var i = 0; i < ConditionCount; i++)
{
builder.Append($"$v{i}: Boolean! ");
}

builder.Append(") {");

for (var i = 0; i < ConditionCount; i++)
{
builder.Append($" f{i}: value @include(if: $v{i})");
}

builder.Append(" }");
return builder.ToString();
}
private static string CreateExpectedResult()
{
var builder = new StringBuilder("{\"data\":{");

for (var i = 0; i < ConditionCount; i++)
{
if (i > 0)
{
builder.Append(',');
}

builder.Append($"\"f{i}\":\"value\"");
}

builder.Append("}}");
return builder.ToString();
}

private sealed class CacheDiagnosticListener : ExecutionDiagnosticEventListener
{
private int _retrievedOperationCount;

public int RetrievedOperationCount => Volatile.Read(ref _retrievedOperationCount);

public override void RetrievedOperationFromCache(RequestContext context)
=> Interlocked.Increment(ref _retrievedOperationCount);

public void Reset()
=> Interlocked.Exchange(ref _retrievedOperationCount, 0);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System.Text;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Toolchains.InProcess.Emit;
using HotChocolate.Execution.Processing;
using HotChocolate.Language;
using HotChocolate.Types;

namespace HotChocolate.Execution.Abstractions.Benchmarks;

[MemoryDiagnoser]
[Config(typeof(BenchmarkConfig))]
public class NarrowConditionOperationBenchmark
{
private const int ConditionCount = 48;

private static readonly string s_documentText = CreateDocument();

private Schema _schema = null!;
private DocumentNode _document = null!;

private sealed class BenchmarkConfig : ManualConfig
{
public BenchmarkConfig()
{
AddJob(
Job.Default
.WithWarmupCount(3)
.WithIterationCount(10)
.WithInvocationCount(16384)
.WithUnrollFactor(1)
.WithToolchain(InProcessEmitToolchain.Instance));
}
}

[GlobalSetup]
public void Setup()
{
_schema = CreateSchema();
_document = Utf8GraphQLParser.Parse(s_documentText);
}

[Benchmark(Baseline = true)]
public Operation Compile_Narrow()
=> OperationCompiler.Compile("benchmark", _document, _schema);

[Benchmark]
public Operation Compile_Narrow_BaselineCopy()
=> OperationCompiler.Compile("benchmark", _document, _schema);

private static Schema CreateSchema()
=> SchemaBuilder.New()
.AddQueryType(
descriptor => descriptor
.Name("Query")
.Field("value")
.Type<NonNullType<StringType>>()
.Resolve("value"))
.Create();

private static string CreateDocument()
{
var builder = new StringBuilder("query(");

for (var i = 0; i < ConditionCount; i++)
{
builder.Append($"$v{i}: Boolean! ");
}

builder.Append(") {");

for (var i = 0; i < ConditionCount; i++)
{
builder.Append($" f{i}: value @include(if: $v{i})");
}

builder.Append(" }");
return builder.ToString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System.Runtime.CompilerServices;

namespace HotChocolate.Execution;

/// <summary>
/// Represents the condition flags evaluated for a request.
/// </summary>
public readonly struct ConditionFlags
{
/// <summary>
/// Initializes a new instance of <see cref="ConditionFlags"/>.
/// </summary>
/// <param name="word0">The flags for condition indexes 0 through 63.</param>
/// <param name="overflow">The flags for condition indexes 64 and above.</param>
public ConditionFlags(ulong word0, ulong[]? overflow = null)
{
Word0 = word0;
Overflow = overflow;
}

/// <summary>
/// Gets the flags for condition indexes 0 through 63.
/// </summary>
public ulong Word0
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
}

/// <summary>
/// Gets the flags for condition indexes 64 and above.
/// </summary>
public ulong[]? Overflow
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,25 @@ public interface ISelection
/// <remarks>
/// For non-conditional selections, this always returns <c>true</c>.
/// </remarks>
[Obsolete("Use IsIncluded(ConditionFlags) instead. This overload throws for operations with more than 64 conditions.")]
bool IsIncluded(ulong includeFlags);

/// <summary>
/// Determines whether this selection should be included based on conditional flags
/// for operations with more than 64 include conditions.
/// </summary>
/// <param name="includeFlags">
/// The conditional inclusion flags.
/// </param>
/// <returns>
/// <c>true</c> if this selection should be executed given the current
/// variable values; otherwise, <c>false</c>.
/// </returns>
/// <remarks>
/// For non-conditional selections, this always returns <c>true</c>.
/// </remarks>
bool IsIncluded(ConditionFlags includeFlags);

/// <summary>
/// Determines whether this selection is deferred based on the <c>@defer</c> directive flags.
/// </summary>
Expand All @@ -148,5 +165,22 @@ public interface ISelection
/// for the initial result.
/// </para>
/// </remarks>
[Obsolete("Use IsDeferred(ConditionFlags) instead. This overload throws for operations with more than 64 conditions.")]
bool IsDeferred(ulong deferFlags);

/// <summary>
/// Determines whether this selection is deferred based on the <c>@defer</c> directive flags
/// for operations with more than 64 defer conditions.
/// </summary>
/// <param name="deferFlags">
/// The defer condition flags.
/// </param>
/// <returns>
/// <c>true</c> if this selection should be deferred and delivered incrementally
/// in a subsequent payload; otherwise, <c>false</c>.
/// </returns>
/// <remarks>
/// For selections without any <c>@defer</c> directive, this always returns <c>false</c>.
/// </remarks>
bool IsDeferred(ConditionFlags deferFlags);
}
Loading
Loading