Skip to content

Commit 1fe5584

Browse files
committed
benchmarks: add governance materialization support
1 parent 645bf7c commit 1fe5584

7 files changed

Lines changed: 283 additions & 0 deletions
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
using BenchmarkDotNet.Attributes;
2+
using ModularityKit.Mutator.Abstractions.Audit;
3+
using ModularityKit.Mutator.Abstractions.History;
4+
using ModularityKit.Mutator.Governance.Abstractions.Queries.Model.Decisions;
5+
using ModularityKit.Mutator.Benchmarks.Governance.Materialization.Support;
6+
7+
namespace ModularityKit.Mutator.Benchmarks.Governance.Materialization;
8+
9+
/// <summary>
10+
/// Benchmarks governance output materialization paths in the governance runtime.
11+
/// </summary>
12+
[BenchmarkCategory("Governance")]
13+
[MemoryDiagnoser]
14+
[InProcess]
15+
public class GovernanceMaterializationOutputBenchmarks
16+
{
17+
private GovernanceMaterializationBenchmarkSupport.GovernanceMaterializationBenchmarkFixture _fixture = null!;
18+
19+
/// <summary>
20+
/// Prepares a representative governed execution result for output materialization benchmarks.
21+
/// </summary>
22+
[GlobalSetup]
23+
public void Setup()
24+
{
25+
_fixture = GovernanceMaterializationBenchmarkSupport.CreateFixture();
26+
}
27+
28+
/// <summary>
29+
/// Measures materialization of the history entry, including copied side effects from governed execution.
30+
/// </summary>
31+
[Benchmark(Baseline = true)]
32+
public MutationHistoryEntry HistoryEntry_FromGovernedExecution()
33+
{
34+
return new MutationHistoryEntry
35+
{
36+
ExecutionId = _fixture.Result.Request.RequestId,
37+
StateId = _fixture.Result.Request.StateId,
38+
Intent = _fixture.Mutation.Intent,
39+
Context = _fixture.Mutation.Context,
40+
Changes = _fixture.Result.MutationResult!.Changes,
41+
SideEffects = _fixture.Result.Request.SideEffects.ToList(),
42+
Timestamp = _fixture.Result.Request.Versioning.ExecutedAt ?? DateTimeOffset.UtcNow,
43+
ExecutionTime = TimeSpan.FromMilliseconds(2)
44+
};
45+
}
46+
47+
/// <summary>
48+
/// Measures materialization of the audit entry produced from the same governed execution output.
49+
/// </summary>
50+
[Benchmark]
51+
public MutationAuditEntry AuditEntry_FromGovernedExecution()
52+
{
53+
return new MutationAuditEntry
54+
{
55+
ExecutionId = _fixture.Result.Request.RequestId,
56+
StateId = _fixture.Result.Request.StateId,
57+
StateType = _fixture.Result.Request.StateType,
58+
MutationIntent = _fixture.Mutation.Intent,
59+
Context = _fixture.Mutation.Context,
60+
Changes = _fixture.Result.MutationResult!.Changes,
61+
IsSuccess = _fixture.Result.MutationResult.IsSuccess,
62+
ErrorMessage = null,
63+
PolicyDecisions = _fixture.Result.MutationResult.PolicyDecisions,
64+
SideEffects = _fixture.Result.Request.SideEffects.ToList(),
65+
Timestamp = _fixture.Result.Request.Versioning.ExecutedAt ?? DateTimeOffset.UtcNow,
66+
Duration = TimeSpan.FromMilliseconds(2),
67+
Metadata = new Dictionary<string, object>
68+
{
69+
["ExecutionKind"] = _fixture.Result.ExecutionKind.ToString(),
70+
["RequestStatus"] = _fixture.Result.Request.Status.ToString()
71+
}
72+
};
73+
}
74+
75+
/// <summary>
76+
/// Measures request decision materialization for downstream consumers.
77+
/// </summary>
78+
[Benchmark]
79+
public IReadOnlyList<MutationRequestDecisionView> DecisionViews_FromGovernedRequest()
80+
{
81+
var request = _fixture.Result.Request;
82+
var views = request.Decisions.Select(decision => new MutationRequestDecisionView
83+
{
84+
Request = request,
85+
Decision = decision
86+
}).ToList();
87+
88+
GC.KeepAlive(views);
89+
return views;
90+
}
91+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using ModularityKit.Mutator.Abstractions;
2+
using ModularityKit.Mutator.Abstractions.Context;
3+
using ModularityKit.Mutator.Abstractions.Engine;
4+
using ModularityKit.Mutator.Benchmarks.Engine;
5+
using ModularityKit.Mutator.Governance.Abstractions.Execution.Model;
6+
using ModularityKit.Mutator.Governance.Abstractions.Requests.Factory;
7+
using ModularityKit.Mutator.Governance.Abstractions.Requests.Model;
8+
using ModularityKit.Mutator.Governance.Abstractions.Resolution.Strategies;
9+
using ModularityKit.Mutator.Governance.Runtime.Execution.Orchestration;
10+
using ModularityKit.Mutator.Governance.Runtime.Resolution.Execution;
11+
using ModularityKit.Mutator.Governance.Runtime.Storage;
12+
13+
namespace ModularityKit.Mutator.Benchmarks.Governance.Materialization.Support;
14+
15+
/// <summary>
16+
/// Builds repeatable governance materialization benchmark fixtures.
17+
/// </summary>
18+
internal static class GovernanceMaterializationBenchmarkSupport
19+
{
20+
public const string StateId = "governance-benchmark:materialization";
21+
22+
/// <summary>
23+
/// Creates a fresh execution result fixture for a single scenario run.
24+
/// </summary>
25+
public static GovernanceMaterializationBenchmarkFixture CreateFixture()
26+
{
27+
var engine = MutationEngineBenchmarkSupport.BuildEngine(MutationEngineOptions.Strict);
28+
var store = new InMemoryMutationRequestStore();
29+
var resolutionManager = new MutationRequestVersionResolutionManager(store, new MutationRequestVersionResolver());
30+
var executionManager = new GovernanceExecutionManager(store, resolutionManager, engine);
31+
var request = store.Create(CreateApprovedRequest<GovernanceMaterializationState, MaterializeGovernanceOutputMutation>("governance-materialization-request"))
32+
.GetAwaiter()
33+
.GetResult();
34+
var mutation = new MaterializeGovernanceOutputMutation(
35+
MutationContext.User("operator", "Operator", "Execute governance materialization benchmark"),
36+
nextVersion: "v11");
37+
38+
var result = executionManager.ExecuteApproved(
39+
request.RequestId,
40+
mutation,
41+
new GovernanceMaterializationState(StateId, 10, "v10"),
42+
governanceContext: mutation.Context,
43+
strategy: VersionedRequestResolutionStrategy.RejectStale)
44+
.GetAwaiter()
45+
.GetResult();
46+
47+
return new GovernanceMaterializationBenchmarkFixture(result, mutation);
48+
}
49+
50+
private static MutationRequest CreateApprovedRequest<TState, TMutation>(string requestId)
51+
where TMutation : IMutation<TState>
52+
=> MutationRequestFactory.Approved<TState, TMutation>(
53+
stateId: StateId,
54+
intent: GovernanceMaterializationScenarioFactory.CreateIntent(),
55+
context: MutationContext.User("requester", "Requester", "Need governance output materialization"),
56+
expectedStateVersion: "v10")
57+
with
58+
{
59+
RequestId = requestId
60+
};
61+
62+
/// <summary>
63+
/// Shared execution result fixture for a governance materialization scenario.
64+
/// </summary>
65+
internal sealed record GovernanceMaterializationBenchmarkFixture(
66+
GovernedExecutionResult<GovernanceMaterializationState> Result,
67+
MaterializeGovernanceOutputMutation Mutation);
68+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using ModularityKit.Mutator.Abstractions.Intent;
2+
3+
namespace ModularityKit.Mutator.Benchmarks.Governance.Materialization.Support;
4+
5+
/// <summary>
6+
/// Creates governance materialization benchmark intent metadata.
7+
/// </summary>
8+
internal static class GovernanceMaterializationScenarioFactory
9+
{
10+
/// <summary>
11+
/// Creates the intent used by governance materialization scenarios.
12+
/// </summary>
13+
public static MutationIntent CreateIntent()
14+
=> new()
15+
{
16+
OperationName = "MaterializeGovernanceOutput",
17+
Category = "Governance",
18+
Description = "Materialize governance output for audit, history, and downstream consumers",
19+
RiskLevel = MutationRiskLevel.Low,
20+
IsReversible = true
21+
};
22+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using ModularityKit.Mutator.Abstractions.Effects;
2+
3+
namespace ModularityKit.Mutator.Benchmarks.Governance.Materialization.Support;
4+
5+
/// <summary>
6+
/// Typed payload used to give governance side effects realistic materialization shape.
7+
/// </summary>
8+
/// <param name="Token">A stable payload token.</param>
9+
/// <param name="Index">The ordinal of the side effect.</param>
10+
[SideEffectDataContract("governance.materialization.side-effect", 1)]
11+
internal sealed record GovernanceMaterializationSideEffectData(string Token, int Index);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using ModularityKit.Mutator.Governance.Abstractions.Execution.Contracts;
2+
3+
namespace ModularityKit.Mutator.Benchmarks.Governance.Materialization.Support;
4+
5+
/// <summary>
6+
/// Minimal versioned state used by governance materialization benchmarks.
7+
/// </summary>
8+
/// <param name="StateId">Stable state identifier.</param>
9+
/// <param name="Value">Benchmark counter value.</param>
10+
/// <param name="Version">Current state version.</param>
11+
internal sealed record GovernanceMaterializationState(string StateId, int Value, string Version) : IVersionedState;
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
using ModularityKit.Mutator.Abstractions.Changes;
2+
using ModularityKit.Mutator.Abstractions.Context;
3+
using ModularityKit.Mutator.Abstractions.Effects;
4+
using ModularityKit.Mutator.Abstractions.Engine;
5+
using ModularityKit.Mutator.Abstractions.Intent;
6+
using ModularityKit.Mutator.Abstractions.Results;
7+
8+
namespace ModularityKit.Mutator.Benchmarks.Governance.Materialization.Support;
9+
10+
/// <summary>
11+
/// Minimal mutation used to produce executed governance output with side effects.
12+
/// </summary>
13+
internal sealed class MaterializeGovernanceOutputMutation(MutationContext context, string nextVersion)
14+
: IMutation<GovernanceMaterializationState>
15+
{
16+
/// <summary>
17+
/// Gets the intent associated with the materialization mutation.
18+
/// </summary>
19+
public MutationIntent Intent { get; } = new()
20+
{
21+
OperationName = "MaterializeGovernanceOutputMutation",
22+
Category = "Governance",
23+
Description = "Produce governed execution output for materialization benchmarks",
24+
RiskLevel = MutationRiskLevel.Low,
25+
IsReversible = true
26+
};
27+
28+
/// <summary>
29+
/// Gets the invocation context for the mutation.
30+
/// </summary>
31+
public MutationContext Context { get; } = context;
32+
33+
/// <summary>
34+
/// Applies the mutation and emits governance side effects.
35+
/// </summary>
36+
public MutationResult<GovernanceMaterializationState> Apply(GovernanceMaterializationState state)
37+
{
38+
var next = state with
39+
{
40+
Value = state.Value + 1,
41+
Version = nextVersion
42+
};
43+
44+
return MutationResult<GovernanceMaterializationState>.Success(
45+
next,
46+
ChangeSet.FromChanges(
47+
StateChange.Modified(nameof(GovernanceMaterializationState.Value), state.Value, next.Value),
48+
StateChange.Modified(nameof(GovernanceMaterializationState.Version), state.Version, next.Version)),
49+
CreateSideEffects());
50+
}
51+
52+
/// <summary>
53+
/// Validates the provided state before mutation execution.
54+
/// </summary>
55+
public ValidationResult Validate(GovernanceMaterializationState state)
56+
{
57+
return state.Value < 0
58+
? ValidationResult.WithError(nameof(GovernanceMaterializationState.Value), "Value must be non-negative.")
59+
: ValidationResult.Success();
60+
}
61+
62+
/// <summary>
63+
/// Simulates the mutation using the same state transition as execution.
64+
/// </summary>
65+
public MutationResult<GovernanceMaterializationState> Simulate(GovernanceMaterializationState state) => Apply(state);
66+
67+
private static IReadOnlyList<SideEffect> CreateSideEffects()
68+
=> [
69+
SideEffect.Create(
70+
"GovernanceOutputMaterialized",
71+
"Governed execution output materialized for history and audit consumers",
72+
new GovernanceMaterializationSideEffectData("history-audit", 1),
73+
SideEffectSeverity.Info),
74+
SideEffect.Critical(
75+
"GovernanceOutputLinked",
76+
"Governed execution output linked for downstream request consumers",
77+
new GovernanceMaterializationSideEffectData("request-link", 2))
78+
];
79+
}

Benchmarks/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ This folder contains BenchmarkDotNet measurements for `ModularityKit.Mutator`.
1919
- governance execution orchestration overhead in the governance runtime
2020
- governance version resolution overhead in the governance runtime
2121
- governance query and read performance overhead in the governance runtime
22+
- governance side effect and decision materialization overhead in the governance runtime
2223

2324
The throughput benchmarks use cloned array backed state so state size effects remain visible in the
2425
actual mutation path rather than being hidden behind an artificial inner loop.

0 commit comments

Comments
 (0)