Skip to content

Commit d453414

Browse files
committed
Added Fusion Cost Analyzer Improvements. (#7867)
1 parent 307c058 commit d453414

22 files changed

+682
-39
lines changed

src/HotChocolate/Core/src/Types/Configuration/AggregateTypeInterceptor.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,22 @@ internal override void InitializeContext(
110110
}
111111
}
112112

113+
internal override bool SkipDirectiveDefinition(DirectiveDefinitionNode node)
114+
{
115+
ref var first = ref GetReference();
116+
var length = _typeInterceptors.Length;
117+
118+
for (var i = 0; i < length; i++)
119+
{
120+
if (Unsafe.Add(ref first, i).SkipDirectiveDefinition(node))
121+
{
122+
return true;
123+
}
124+
}
125+
126+
return false;
127+
}
128+
113129
public override void OnBeforeDiscoverTypes()
114130
{
115131
ref var first = ref GetReference();

src/HotChocolate/Core/src/Types/Configuration/TypeInterceptor.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ public virtual void OnBeforeDiscoverTypes() { }
7474
/// </summary>
7575
public virtual void OnAfterDiscoverTypes() { }
7676

77+
internal virtual bool SkipDirectiveDefinition(DirectiveDefinitionNode node)
78+
=> false;
79+
7780
/// <summary>
7881
/// This event is triggered after the type instance was created but before
7982
/// any type definition was initialized.

src/HotChocolate/Core/src/Types/Types/Factories/SchemaSyntaxVisitor.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,11 @@ protected override ISyntaxVisitorAction VisitChildren(
171171
goto EXIT;
172172
}
173173

174+
if(context.DescriptorContext.TypeInterceptor.SkipDirectiveDefinition(node))
175+
{
176+
goto EXIT;
177+
}
178+
174179
context.Types.Add(
175180
TypeReference.Create(
176181
_directiveTypeFactory.Create(

src/HotChocolate/CostAnalysis/src/CostAnalysis/CostAnalyzerMiddleware.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public async ValueTask InvokeAsync(IRequestContext context)
3636
}
3737

3838
var requestOptions = context.TryGetCostOptions() ?? options;
39-
var mode = context.GetCostAnalyzerMode(requestOptions.EnforceCostLimits);
39+
var mode = context.GetCostAnalyzerMode(requestOptions);
4040

4141
if (mode == CostAnalyzerMode.Skip)
4242
{

src/HotChocolate/CostAnalysis/src/CostAnalysis/CostTypeInterceptor.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,13 @@ public override void OnBeforeCompleteType(ITypeCompletionContext completionConte
171171
}
172172
}
173173

174+
internal sealed class CostDirectiveTypeInterceptor : TypeInterceptor
175+
{
176+
internal override bool SkipDirectiveDefinition(DirectiveDefinitionNode node)
177+
=> node.Name.Value.Equals("cost", StringComparison.Ordinal)
178+
|| node.Name.Value.Equals("listSize", StringComparison.Ordinal);
179+
}
180+
174181
file static class Extensions
175182
{
176183
public static bool HasCostDirective(this IHasDirectiveDefinition directiveProvider)

src/HotChocolate/CostAnalysis/src/CostAnalysis/DependencyInjection/CostAnalyzerRequestExecutorBuilderExtensions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,14 @@ public static IRequestExecutorBuilder AddCostAnalyzer(this IRequestExecutorBuild
5656
requestOptions.MaxFieldCost,
5757
requestOptions.MaxTypeCost,
5858
requestOptions.EnforceCostLimits,
59+
requestOptions.SkipAnalyzer,
5960
requestOptions.Filtering.VariableMultiplier);
6061
});
6162
})
6263
.AddDirectiveType<CostDirectiveType>()
6364
.AddDirectiveType<ListSizeDirectiveType>()
6465
.TryAddTypeInterceptor<CostTypeInterceptor>()
66+
.TryAddTypeInterceptor<CostDirectiveTypeInterceptor>()
6567

6668
// we are replacing the default pipeline if the cost analyzer is added.
6769
.Configure(c => c.DefaultPipelineFactory = AddDefaultPipeline);

src/HotChocolate/CostAnalysis/src/CostAnalysis/Extensions/CostAnalyzerRequestContextExtensions.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,21 +58,26 @@ public static CostMetrics GetCostMetrics(
5858

5959
internal static CostAnalyzerMode GetCostAnalyzerMode(
6060
this IRequestContext context,
61-
bool enforceCostLimits)
61+
RequestCostOptions options)
6262
{
6363
if (context is null)
6464
{
6565
throw new ArgumentNullException(nameof(context));
6666
}
6767

68+
if (options.SkipAnalyzer)
69+
{
70+
return CostAnalyzerMode.Skip;
71+
}
72+
6873
if (context.ContextData.ContainsKey(WellKnownContextData.ValidateCost))
6974
{
7075
return CostAnalyzerMode.Analyze | CostAnalyzerMode.Report;
7176
}
7277

7378
var flags = CostAnalyzerMode.Analyze;
7479

75-
if (enforceCostLimits)
80+
if (options.EnforceCostLimits)
7681
{
7782
flags |= CostAnalyzerMode.Enforce;
7883
}

src/HotChocolate/CostAnalysis/src/CostAnalysis/HotChocolate.CostAnalysis.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
<ItemGroup>
44
<InternalsVisibleTo Include="HotChocolate.CostAnalysis.Tests" />
5+
<InternalsVisibleTo Include="HotChocolate.Fusion" />
56
</ItemGroup>
67

78
<ItemGroup>

src/HotChocolate/CostAnalysis/src/CostAnalysis/Options/CostOptions.cs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ namespace HotChocolate.CostAnalysis;
55
/// </summary>
66
public sealed class CostOptions
77
{
8+
private bool _skipAnalyzer = false;
9+
private bool _enforceCostLimits = true;
10+
811
/// <summary>
912
/// Gets or sets the maximum allowed field cost.
1013
/// </summary>
@@ -18,7 +21,35 @@ public sealed class CostOptions
1821
/// <summary>
1922
/// Defines if the analyzer shall enforce cost limits.
2023
/// </summary>
21-
public bool EnforceCostLimits { get; set; } = true;
24+
public bool EnforceCostLimits
25+
{
26+
get => _enforceCostLimits;
27+
set
28+
{
29+
if(value)
30+
{
31+
SkipAnalyzer = false;
32+
}
33+
34+
_enforceCostLimits = value;
35+
}
36+
}
37+
38+
/// <summary>
39+
/// Skips the cost analyzer.
40+
/// </summary>
41+
public bool SkipAnalyzer
42+
{
43+
get => _skipAnalyzer;
44+
set
45+
{
46+
if(value)
47+
{
48+
EnforceCostLimits = false;
49+
}
50+
_skipAnalyzer = value;
51+
}
52+
}
2253

2354
/// <summary>
2455
/// Defines if cost defaults shall be applied to the schema.

src/HotChocolate/CostAnalysis/src/CostAnalysis/Options/RequestCostOptions.cs

Lines changed: 142 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,145 @@ namespace HotChocolate.CostAnalysis;
33
/// <summary>
44
/// Request options for cost analysis.
55
/// </summary>
6-
/// <param name="MaxFieldCost">
7-
/// The maximum allowed field cost.
8-
/// </param>
9-
/// <param name="MaxTypeCost">
10-
/// The maximum allowed type cost.
11-
/// </param>
12-
/// <param name="EnforceCostLimits">
13-
/// Defines if the analyzer shall enforce cost limits.
14-
/// </param>
15-
/// <param name="FilterVariableMultiplier">
16-
/// The filter variable multiplier.
17-
/// </param>
18-
public record RequestCostOptions(
19-
double MaxFieldCost,
20-
double MaxTypeCost,
21-
bool EnforceCostLimits,
22-
int? FilterVariableMultiplier);
6+
public record RequestCostOptions
7+
{
8+
/// <summary>
9+
/// Request options for cost analysis.
10+
/// </summary>
11+
/// <param name="maxFieldCost">
12+
/// The maximum allowed field cost.
13+
/// </param>
14+
/// <param name="maxTypeCost">
15+
/// The maximum allowed type cost.
16+
/// </param>
17+
/// <param name="enforceCostLimits">
18+
/// Defines if the analyzer shall enforce cost limits.
19+
/// </param>
20+
/// <param name="filterVariableMultiplier">
21+
/// The filter variable multiplier.
22+
/// </param>
23+
public RequestCostOptions(
24+
double maxFieldCost,
25+
double maxTypeCost,
26+
bool enforceCostLimits,
27+
int? filterVariableMultiplier)
28+
{
29+
MaxFieldCost = maxFieldCost;
30+
MaxTypeCost = maxTypeCost;
31+
EnforceCostLimits = enforceCostLimits;
32+
FilterVariableMultiplier = filterVariableMultiplier;
33+
}
34+
35+
/// <summary>
36+
/// Gets the maximum allowed field cost.
37+
/// </summary>
38+
/// <param name="maxFieldCost">
39+
/// The maximum allowed field cost.
40+
/// </param>
41+
/// <param name="maxTypeCost">
42+
/// The maximum allowed type cost.
43+
/// </param>
44+
/// <param name="enforceCostLimits">
45+
/// Defines if the analyzer shall enforce cost limits.
46+
/// </param>
47+
/// <param name="skipAnalyzer">
48+
/// Defines if the cost analyzer shall be skipped.
49+
/// </param>
50+
/// <param name="filterVariableMultiplier">
51+
/// The filter variable multiplier.
52+
/// </param>
53+
public RequestCostOptions(
54+
double maxFieldCost,
55+
double maxTypeCost,
56+
bool enforceCostLimits,
57+
bool skipAnalyzer,
58+
int? filterVariableMultiplier)
59+
{
60+
MaxFieldCost = maxFieldCost;
61+
MaxTypeCost = maxTypeCost;
62+
EnforceCostLimits = enforceCostLimits;
63+
SkipAnalyzer = skipAnalyzer;
64+
FilterVariableMultiplier = filterVariableMultiplier;
65+
}
66+
67+
/// <summary>
68+
/// Gets the maximum allowed field cost.
69+
/// </summary>
70+
public double MaxFieldCost { get; init; }
71+
72+
/// <summary>
73+
/// Gets the maximum allowed type cost.
74+
/// </summary>
75+
public double MaxTypeCost { get; init; }
76+
77+
/// <summary>
78+
/// Defines if the analyzer shall enforce cost limits.
79+
/// </summary>
80+
public bool EnforceCostLimits
81+
{
82+
get;
83+
init
84+
{
85+
if (value)
86+
{
87+
SkipAnalyzer = false;
88+
}
89+
90+
field = value;
91+
}
92+
}
93+
94+
/// <summary>
95+
/// Defines if the cost analyzer shall be skipped.
96+
/// </summary>
97+
public bool SkipAnalyzer
98+
{
99+
get;
100+
init
101+
{
102+
if (value)
103+
{
104+
EnforceCostLimits = false;
105+
}
106+
107+
field = value;
108+
}
109+
}
110+
111+
/// <summary>
112+
/// Gets the filter variable multiplier.
113+
/// </summary>
114+
public int? FilterVariableMultiplier { get; init; }
115+
116+
/// <summary>
117+
/// Deconstructs the request options.
118+
/// </summary>
119+
/// <param name="maxFieldCost">
120+
/// The maximum allowed field cost.
121+
/// </param>
122+
/// <param name="maxTypeCost">
123+
/// The maximum allowed type cost.
124+
/// </param>
125+
/// <param name="enforceCostLimits">
126+
/// Defines if the analyzer shall enforce cost limits.
127+
/// </param>
128+
/// <param name="skipAnalyzer">
129+
/// Defines if the cost analyzer shall be skipped.
130+
/// </param>
131+
/// <param name="filterVariableMultiplier">
132+
/// The filter variable multiplier.
133+
/// </param>
134+
public void Deconstruct(
135+
out double maxFieldCost,
136+
out double maxTypeCost,
137+
out bool enforceCostLimits,
138+
out bool skipAnalyzer,
139+
out int? filterVariableMultiplier)
140+
{
141+
maxFieldCost = MaxFieldCost;
142+
maxTypeCost = MaxTypeCost;
143+
enforceCostLimits = EnforceCostLimits;
144+
skipAnalyzer = SkipAnalyzer;
145+
filterVariableMultiplier = FilterVariableMultiplier;
146+
}
147+
}

0 commit comments

Comments
 (0)