Skip to content

Commit 9b1cec8

Browse files
authored
Perf: Eliminate audit and history entry allocations when auditor/store is disabled (#91)
2 parents bfad86f + bb841ae commit 9b1cec8

4 files changed

Lines changed: 94 additions & 7 deletions

File tree

src/Abstractions/Audit/IMutationAuditor.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ namespace ModularityKit.Mutator.Abstractions.Audit;
1111
/// </remarks>
1212
public interface IMutationAuditor
1313
{
14+
/// <summary>
15+
/// When <see langword="false" />, the runtime may skip creating audit entries for this auditor.
16+
/// </summary>
17+
bool IsEnabled => true;
18+
1419
/// <summary>
1520
/// Records an audit entry for a mutation.
1621
/// </summary>

src/Abstractions/History/IMutationHistoryStore.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ namespace ModularityKit.Mutator.Abstractions.History;
2727
/// </example>
2828
public interface IMutationHistoryStore
2929
{
30+
/// <summary>
31+
/// When <see langword="false" />, the runtime may skip creating history entries for this store.
32+
/// </summary>
33+
bool IsEnabled => true;
34+
3035
/// <summary>
3136
/// Persists a mutation history entry.
3237
/// </summary>

src/Runtime/Diagnostics/MutationAuditEntryFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ public static MutationHistoryEntry CreateHistoryEntry<TState>(
116116
Intent = mutation.Intent,
117117
Context = mutation.Context,
118118
Changes = result.Changes,
119-
SideEffects = result.SideEffects.ToList(),
119+
SideEffects = result.SideEffects,
120120
Timestamp = mutation.Context.Timestamp,
121121
ExecutionTime = duration
122122
};

src/Runtime/Internal/Execution/MutationExecutionOutcomeProcessor.cs

Lines changed: 83 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,17 @@
1212
namespace ModularityKit.Mutator.Runtime.Internal.Execution;
1313

1414
/// <summary>
15-
/// Handles blocked, failed, and completed mutation outcomes after policy evaluation and execution.
15+
/// Processes mutation outcomes after policy evaluation and mutation execution.
1616
/// </summary>
17+
/// <remarks>
18+
/// Handles policy blocked, validation failed, successfully completed, and finalized
19+
/// mutation outcomes. Coordinates interceptor notifications, audit recording,
20+
/// mutation history persistence, and metrics collection.
21+
/// </remarks>
22+
/// <param name="interceptorPipeline">Pipeline responsible for notifying registered mutation interceptors about execution outcomes.</param>
23+
/// <param name="auditor">Auditor responsible for recording mutation success and failure audit entries. </param>
24+
/// <param name="historyStore">Store responsible for persisting mutation history for committed mutations.</param>
25+
/// <param name="metricsCollector">Collector responsible for recording mutation execution metrics.</param>
1726
internal sealed class MutationExecutionOutcomeProcessor(
1827
IInterceptorPipeline interceptorPipeline,
1928
IMutationAuditor auditor,
@@ -28,8 +37,15 @@ internal sealed class MutationExecutionOutcomeProcessor(
2837
private readonly IMetricsCollector _metricsCollector = metricsCollector ?? throw new ArgumentNullException(nameof(metricsCollector));
2938

3039
/// <summary>
31-
/// Handles a policy-blocked mutation result.
40+
/// Handles mutation that was blocked by a policy decision.
3241
/// </summary>
42+
/// <typeparam name="TState">The type of state associated with the mutation.</typeparam>
43+
/// <param name="executionContext">
44+
/// The context containing the mutation, current state, execution metadata,
45+
/// cancellation token, and metrics information.
46+
/// </param>
47+
/// <param name="policyDecision">The policy decision that blocked the mutation.</param>
48+
/// <returns>Policy blocked mutation result with audit and execution metrics finalized.</returns>
3349
public async Task<MutationResult<TState>> HandleBlockedPolicyAsync<TState>(
3450
MutationExecutionContext<TState> executionContext,
3551
PolicyDecision policyDecision)
@@ -53,9 +69,17 @@ await AuditFailureAsync(
5369
return await FinalizeResultAsync(executionContext, blockedResult).ConfigureAwait(false);
5470
}
5571

72+
5673
/// <summary>
57-
/// Handles a validation-failed mutation result.
74+
/// Handles mutation that failed validation.
5875
/// </summary>
76+
/// <typeparam name="TState">The type of state associated with the mutation.</typeparam>
77+
/// <param name="executionContext">
78+
/// The context containing the mutation, current state, execution metadata,
79+
/// cancellation token, and metrics information.
80+
/// </param>
81+
/// <param name="validationFailureResult">The mutation result containing validation errors.</param>
82+
/// <returns>The validation failure result with audit and execution metrics finalized.</returns>
5983
public async Task<MutationResult<TState>> HandleValidationFailureAsync<TState>(
6084
MutationExecutionContext<TState> executionContext,
6185
MutationResult<TState> validationFailureResult)
@@ -70,8 +94,20 @@ await AuditFailureAsync(
7094
}
7195

7296
/// <summary>
73-
/// Completes a mutation result after execution and policy modifications.
97+
/// Completes mutation execution after applying policy modifications and
98+
/// processing successful mutation outcomes.
7499
/// </summary>
100+
/// <typeparam name="TState">The type of state associated with the mutation.</typeparam>
101+
/// <param name="executionContext">
102+
/// The context containing the mutation, current state, execution metadata,
103+
/// cancellation token, and metrics information.
104+
/// </param>
105+
/// <param name="mutationResult">The result produced by the mutation execution.</param>
106+
/// <param name="policyDecision">The policy decision containing any modifications to apply to the mutation result.</param>
107+
/// <returns>
108+
/// The finalized mutation result after interceptor notification, auditing,
109+
/// optional history persistence, and metrics processing.
110+
/// </returns>
75111
public async Task<MutationResult<TState>> CompleteMutationAsync<TState>(
76112
MutationExecutionContext<TState> executionContext,
77113
MutationResult<TState> mutationResult,
@@ -110,8 +146,13 @@ await StoreInHistoryAsync(
110146
}
111147

112148
/// <summary>
113-
/// Finalizes a runtime result by recording metrics and attaching total execution time.
149+
/// Finalizes mutation result by recording execution metrics and attaching
150+
/// the total execution duration to the result.
114151
/// </summary>
152+
/// <typeparam name="TState">The type of state associated with the mutation.</typeparam>
153+
/// <param name="executionContext">The context containing execution timing and metrics information.</param>
154+
/// <param name="result">The mutation result to finalize.</param>
155+
/// <returns>The mutation result with finalized execution metrics.</returns>
115156
public async Task<MutationResult<TState>> FinalizeResultAsync<TState>(
116157
MutationExecutionContext<TState> executionContext,
117158
MutationResult<TState> result)
@@ -133,13 +174,25 @@ await _metricsCollector.RecordAsync(
133174
};
134175
}
135176

177+
/// <summary>
178+
/// Records successful mutation execution in the audit system when auditing is enabled.
179+
/// </summary>
180+
/// <typeparam name="TState">The type of state associated with the mutation.</typeparam>
181+
/// <param name="mutation">The mutation that was executed.</param>
182+
/// <param name="result">The resulting mutation result.</param>
183+
/// <param name="policyDecision">The policy decision applied to the mutation.</param>
184+
/// <param name="executionId">The unique identifier of the mutation execution.</param>
185+
/// <param name="duration">The total execution duration.</param>
136186
private async Task AuditSuccessAsync<TState>(
137187
IMutation<TState> mutation,
138188
MutationResult<TState> result,
139189
PolicyDecision policyDecision,
140190
string executionId,
141191
TimeSpan duration)
142192
{
193+
if (!_auditor.IsEnabled)
194+
return;
195+
143196
var entry = MutationAuditEntryFactory.CreateSuccess(
144197
mutation,
145198
result,
@@ -150,12 +203,23 @@ private async Task AuditSuccessAsync<TState>(
150203
await _auditor.AuditAsync(entry).ConfigureAwait(false);
151204
}
152205

206+
/// <summary>
207+
/// Records failed mutation execution in the audit system when auditing is enabled.
208+
/// </summary>
209+
/// <typeparam name="TState">The type of state associated with the mutation.</typeparam>
210+
/// <param name="mutation">The mutation that failed.</param>
211+
/// <param name="result">The resulting mutation failure.</param>
212+
/// <param name="executionId">The unique identifier of the mutation execution.</param>
213+
/// <param name="duration">The total execution duration.</param>
153214
private async Task AuditFailureAsync<TState>(
154215
IMutation<TState> mutation,
155216
MutationResult<TState> result,
156217
string executionId,
157218
TimeSpan duration)
158219
{
220+
if (!_auditor.IsEnabled)
221+
return;
222+
159223
var entry = MutationAuditEntryFactory.CreateFailure(
160224
mutation,
161225
result,
@@ -165,13 +229,26 @@ private async Task AuditFailureAsync<TState>(
165229
await _auditor.AuditAsync(entry).ConfigureAwait(false);
166230
}
167231

168-
private async Task StoreInHistoryAsync<TState>(
232+
/// <summary>
233+
/// Stores successful committed mutation in the mutation history store when history
234+
/// persistence is enabled and state identifier can be resolved.
235+
/// </summary>
236+
/// <typeparam name="TState">The type of state associated with the mutation.</typeparam>
237+
/// <param name="mutation">The mutation that was executed.</param>
238+
/// <param name="result">The resulting mutation result.</param>
239+
/// <param name="executionId">The unique identifier of the mutation execution.</param>
240+
/// <param name="duration">The total execution duration.</param>
241+
/// <param name="cancellationToken">Token that can be used to cancel the history persistence operation. </param>
242+
private async Task StoreInHistoryAsync<TState>(
169243
IMutation<TState> mutation,
170244
MutationResult<TState> result,
171245
string executionId,
172246
TimeSpan duration,
173247
CancellationToken cancellationToken)
174248
{
249+
if (!_historyStore.IsEnabled)
250+
return;
251+
175252
var stateId = MutationAuditEntryFactory.ResolveStateId(mutation.Context);
176253
if (string.IsNullOrEmpty(stateId))
177254
return;

0 commit comments

Comments
 (0)