Skip to content

Commit f7611fa

Browse files
authored
[Fusion] Added Result Pooling (#8430)
1 parent 6734438 commit f7611fa

18 files changed

+518
-60
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
namespace HotChocolate.Fusion.Configuration;
2+
3+
public sealed class FusionMemoryPoolOptions
4+
{
5+
public int ObjectBatchSize { get; set; } = 64;
6+
public int DefaultObjectCapacity { get; set; } = 64;
7+
public int MaxAllowedObjectCapacity { get; set; } = 512;
8+
9+
public int ListBatchSize { get; set; } = 128;
10+
public int DefaultListCapacity { get; set; } = 64;
11+
public int MaxAllowedListCapacity { get; set; } = 512;
12+
13+
public int LeafFieldBatchSize { get; set; } = 512;
14+
public int ListFieldBatchSize { get; set; } = 128;
15+
public int ObjectFieldBatchSize { get; set; } = 128;
16+
17+
public FusionMemoryPoolOptions Clone()
18+
{
19+
return new FusionMemoryPoolOptions
20+
{
21+
ObjectBatchSize = ObjectBatchSize,
22+
DefaultObjectCapacity = DefaultObjectCapacity,
23+
MaxAllowedObjectCapacity = MaxAllowedObjectCapacity,
24+
ListBatchSize = ListBatchSize,
25+
DefaultListCapacity = DefaultListCapacity,
26+
MaxAllowedListCapacity = MaxAllowedListCapacity,
27+
LeafFieldBatchSize = LeafFieldBatchSize,
28+
ListFieldBatchSize = ListFieldBatchSize,
29+
ObjectFieldBatchSize = ObjectFieldBatchSize
30+
};
31+
}
32+
}

src/HotChocolate/Fusion-vnext/src/Fusion.Execution/DependencyInjection/HotChocolateFusionServiceCollectionExtensions.cs

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,19 @@
44
using HotChocolate.Fusion.Execution;
55
using HotChocolate.Fusion.Execution.Clients;
66
using Microsoft.Extensions.DependencyInjection.Extensions;
7+
using Microsoft.Extensions.ObjectPool;
78

89
namespace Microsoft.Extensions.DependencyInjection;
910

1011
public static class HotChocolateFusionServiceCollectionExtensions
1112
{
1213
public static IFusionGatewayBuilder AddGraphQLGateway(
1314
this IServiceCollection services,
14-
string? name = null)
15+
string? name = null,
16+
FusionMemoryPoolOptions? options = null)
1517
{
1618
name ??= ISchemaDefinition.DefaultName;
19+
options ??= new FusionMemoryPoolOptions();
1720

1821
ArgumentNullException.ThrowIfNull(services);
1922
ArgumentException.ThrowIfNullOrEmpty(name);
@@ -22,6 +25,7 @@ public static IFusionGatewayBuilder AddGraphQLGateway(
2225

2326
AddRequestExecutorManager(services);
2427
AddSourceSchemaScope(services);
28+
AddResultObjectPools(services, options.Clone());
2529

2630
return CreateBuilder(services, name);
2731
}
@@ -44,6 +48,94 @@ private static void AddSourceSchemaScope(
4448
sp.GetRequiredService<IHttpClientFactory>()));
4549
}
4650

51+
private static void AddResultObjectPools(
52+
IServiceCollection services,
53+
FusionMemoryPoolOptions options)
54+
{
55+
services.TryAddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
56+
57+
services.TryAddSingleton(options);
58+
59+
services.TryAddSingleton(static provider =>
60+
{
61+
var options = provider.GetRequiredService<FusionMemoryPoolOptions>();
62+
var providerPool = provider.GetRequiredService<ObjectPoolProvider>();
63+
return providerPool.Create(new ResultDataBatchPoolPolicy<ObjectResult>(
64+
options.ObjectBatchSize,
65+
options.DefaultObjectCapacity,
66+
options.MaxAllowedObjectCapacity));
67+
});
68+
69+
services.TryAddSingleton(static provider =>
70+
{
71+
var options = provider.GetRequiredService<FusionMemoryPoolOptions>();
72+
var providerPool = provider.GetRequiredService<ObjectPoolProvider>();
73+
return providerPool.Create(new ResultDataBatchPoolPolicy<LeafFieldResult>(options.LeafFieldBatchSize));
74+
});
75+
76+
services.TryAddSingleton(static provider =>
77+
{
78+
var options = provider.GetRequiredService<FusionMemoryPoolOptions>();
79+
var providerPool = provider.GetRequiredService<ObjectPoolProvider>();
80+
return providerPool.Create(new ResultDataBatchPoolPolicy<ListFieldResult>(options.ListFieldBatchSize));
81+
});
82+
83+
services.TryAddSingleton(static provider =>
84+
{
85+
var options = provider.GetRequiredService<FusionMemoryPoolOptions>();
86+
var providerPool = provider.GetRequiredService<ObjectPoolProvider>();
87+
return providerPool.Create(new ResultDataBatchPoolPolicy<ObjectFieldResult>(options.ObjectFieldBatchSize));
88+
});
89+
90+
services.TryAddSingleton(static provider =>
91+
{
92+
var options = provider.GetRequiredService<FusionMemoryPoolOptions>();
93+
var providerPool = provider.GetRequiredService<ObjectPoolProvider>();
94+
return providerPool.Create(new ResultDataBatchPoolPolicy<ObjectListResult>(
95+
options.ListBatchSize,
96+
options.DefaultListCapacity,
97+
options.MaxAllowedListCapacity));
98+
});
99+
100+
services.TryAddSingleton(static provider =>
101+
{
102+
var options = provider.GetRequiredService<FusionMemoryPoolOptions>();
103+
var providerPool = provider.GetRequiredService<ObjectPoolProvider>();
104+
return providerPool.Create(new ResultDataBatchPoolPolicy<NestedListResult>(
105+
options.ListBatchSize,
106+
options.DefaultListCapacity,
107+
options.MaxAllowedListCapacity));
108+
});
109+
110+
services.TryAddSingleton(static provider =>
111+
{
112+
var options = provider.GetRequiredService<FusionMemoryPoolOptions>();
113+
var providerPool = provider.GetRequiredService<ObjectPoolProvider>();
114+
return providerPool.Create(new ResultDataBatchPoolPolicy<LeafListResult>(
115+
options.ListBatchSize,
116+
options.DefaultListCapacity,
117+
options.MaxAllowedListCapacity));
118+
});
119+
120+
services.TryAddSingleton(static provider =>
121+
{
122+
var providerPool = provider.GetRequiredService<ObjectPoolProvider>();
123+
return providerPool.Create(new ResultPoolSessionPolicy(
124+
provider.GetRequiredService<ObjectPool<ResultDataBatch<ObjectResult>>>(),
125+
provider.GetRequiredService<ObjectPool<ResultDataBatch<LeafFieldResult>>>(),
126+
provider.GetRequiredService<ObjectPool<ResultDataBatch<ListFieldResult>>>(),
127+
provider.GetRequiredService<ObjectPool<ResultDataBatch<ObjectFieldResult>>>(),
128+
provider.GetRequiredService<ObjectPool<ResultDataBatch<ObjectListResult>>>(),
129+
provider.GetRequiredService<ObjectPool<ResultDataBatch<NestedListResult>>>(),
130+
provider.GetRequiredService<ObjectPool<ResultDataBatch<LeafListResult>>>()));
131+
});
132+
133+
services.TryAddScoped(static provider =>
134+
new ResultPoolSessionHolder(provider.GetRequiredService<ObjectPool<ResultPoolSession>>()));
135+
136+
services.TryAddScoped(static provider => provider.GetRequiredService<ResultPoolSessionHolder>().Session);
137+
}
138+
47139
private static DefaultFusionGatewayBuilder CreateBuilder(
48140
IServiceCollection services,
49141
string name)

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using HotChocolate.Fusion.Execution;
21
using HotChocolate.Fusion.Execution.Nodes;
32

43
namespace Microsoft.Extensions.ObjectPool;

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ public sealed class OperationPlanContext : IFeatureProvider, IAsyncDisposable
1717
public OperationPlanContext(
1818
OperationExecutionPlan operationPlan,
1919
IVariableValueCollection variables,
20-
RequestContext requestContext)
20+
RequestContext requestContext,
21+
ResultPoolSession resultPoolSession)
2122
{
2223
OperationPlan = operationPlan;
2324
RequestContext = requestContext;
@@ -26,7 +27,7 @@ public OperationPlanContext(
2627
// TODO : fully implement and inject ResultPoolSession
2728
_resultStore = new FetchResultStore(
2829
RequestContext.Schema,
29-
new ResultPoolSession(),
30+
resultPoolSession,
3031
operationPlan.Operation,
3132
operationPlan.Operation.CreateIncludeFlags(variables));
3233

src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Pipeline/OperationExecutionMiddleware.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using HotChocolate.Execution;
2+
using Microsoft.Extensions.DependencyInjection;
23

34
namespace HotChocolate.Fusion.Execution.Pipeline;
45

@@ -8,6 +9,7 @@ internal sealed class OperationExecutionMiddleware
89

910
public async ValueTask InvokeAsync(
1011
RequestContext context,
12+
ResultPoolSession resultPoolSession,
1113
RequestDelegate next,
1214
CancellationToken cancellationToken)
1315
{
@@ -21,7 +23,8 @@ public async ValueTask InvokeAsync(
2123
var operationPlanContext = new OperationPlanContext(
2224
operationPlan,
2325
context.VariableValues[0],
24-
context);
26+
context,
27+
resultPoolSession);
2528

2629
context.Result = await _queryExecutor.QueryAsync(operationPlanContext, cancellationToken);
2730

@@ -31,10 +34,14 @@ public async ValueTask InvokeAsync(
3134
public static RequestMiddlewareConfiguration Create()
3235
{
3336
return new RequestMiddlewareConfiguration(
34-
(fc, next) =>
37+
(_, next) =>
3538
{
3639
var middleware = new OperationExecutionMiddleware();
37-
return context => middleware.InvokeAsync(context, next, context.RequestAborted);
40+
return context => middleware.InvokeAsync(
41+
context,
42+
context.RequestServices.GetRequiredService<ResultPoolSession>(),
43+
next,
44+
context.RequestAborted);
3845
},
3946
nameof(OperationExecutionMiddleware));
4047
}

src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Results/FetchResultStore.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using System.Collections.Immutable;
44
using System.Runtime.CompilerServices;
55
using System.Runtime.InteropServices;
6-
using System.Text;
76
using System.Text.Json;
87
using HotChocolate.Buffers;
98
using HotChocolate.Fusion.Execution.Clients;

src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Results/LeafListResult.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ public sealed class LeafListResult : ListResult
1313
/// </summary>
1414
public List<JsonElement> Items { get; } = [];
1515

16+
/// <summary>
17+
/// Gets the capacity of the leaf list result.
18+
/// </summary>
19+
public override int Capacity
20+
{
21+
get => Items.Capacity;
22+
protected set => Items.Capacity = value;
23+
}
24+
1625
/// <summary>
1726
/// Adds a null value to the list.
1827
/// </summary>
@@ -69,12 +78,6 @@ public override void WriteTo(
6978
public override bool Reset()
7079
{
7180
Items.Clear();
72-
73-
if (Items.Capacity > 512)
74-
{
75-
Items.Capacity = 512;
76-
}
77-
7881
return base.Reset();
7982
}
8083
}

src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Results/ListResult.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ namespace HotChocolate.Fusion.Execution;
77
/// </summary>
88
public abstract class ListResult : ResultData
99
{
10+
private int _defaultCapacity = 64;
11+
private int _maxAllowedCapacity = 512;
12+
1013
/// <summary>
1114
/// Gets the type of the list result.
1215
/// </summary>
@@ -17,6 +20,11 @@ public abstract class ListResult : ResultData
1720
/// </summary>
1821
public IType ElementType { get; private set; } = null!;
1922

23+
/// <summary>
24+
/// Gets or sets the capacity of the list result.
25+
/// </summary>
26+
public abstract int Capacity { get; protected set; }
27+
2028
/// <summary>
2129
/// Initializes the <see cref="ListResult"/> with the specified type.
2230
/// </summary>
@@ -31,6 +39,21 @@ public void Initialize(IType type)
3139
ElementType = type.ElementType();
3240
}
3341

42+
internal override void SetCapacity(int capacity, int maxAllowedCapacity)
43+
{
44+
ArgumentOutOfRangeException.ThrowIfLessThan(capacity, 8);
45+
ArgumentOutOfRangeException.ThrowIfLessThan(maxAllowedCapacity, 16);
46+
47+
if (capacity > maxAllowedCapacity)
48+
{
49+
throw new ArgumentException($"Capacity cannot be greater than {maxAllowedCapacity}");
50+
}
51+
52+
_defaultCapacity = capacity;
53+
_maxAllowedCapacity = maxAllowedCapacity;
54+
Capacity = capacity;
55+
}
56+
3457
/// <summary>
3558
/// Resets the <see cref="ListResult"/> to its initial state.
3659
/// </summary>
@@ -39,6 +62,11 @@ public override bool Reset()
3962
Type = null!;
4063
ElementType = null!;
4164

65+
if (Capacity > _maxAllowedCapacity)
66+
{
67+
Capacity = _defaultCapacity;
68+
}
69+
4270
return base.Reset();
4371
}
4472
}

src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Results/NestedListResult.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ public sealed class NestedListResult : ListResult
1313
/// </summary>
1414
public List<ListResult?> Items { get; } = [];
1515

16+
/// <summary>
17+
/// Gets the capacity of the leaf list result.
18+
/// </summary>
19+
public override int Capacity
20+
{
21+
get => Items.Capacity;
22+
protected set => Items.Capacity = value;
23+
}
24+
1625
/// <summary>
1726
/// Adds a null value to the list.
1827
/// </summary>
@@ -88,12 +97,6 @@ public override void WriteTo(
8897
public override bool Reset()
8998
{
9099
Items.Clear();
91-
92-
if (Items.Capacity > 512)
93-
{
94-
Items.Capacity = 512;
95-
}
96-
97100
return base.Reset();
98101
}
99102
}

src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Results/ObjectListResult.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ public sealed class ObjectListResult : ListResult
1313
/// </summary>
1414
public List<ObjectResult?> Items { get; } = [];
1515

16+
/// <summary>
17+
/// Gets the capacity of the leaf list result.
18+
/// </summary>
19+
public override int Capacity
20+
{
21+
get => Items.Capacity;
22+
protected set => Items.Capacity = value;
23+
}
24+
1625
/// <summary>
1726
/// Adds a null value to the list.
1827
/// </summary>
@@ -88,12 +97,6 @@ public override void WriteTo(
8897
public override bool Reset()
8998
{
9099
Items.Clear();
91-
92-
if (Items.Capacity > 512)
93-
{
94-
Items.Capacity = 512;
95-
}
96-
97100
return base.Reset();
98101
}
99102
}

0 commit comments

Comments
 (0)