Skip to content

Commit cbb6ac2

Browse files
authored
Feat: add governance query and materialization benchmarks (#82)
2 parents e86b7d7 + 1fe5584 commit cbb6ac2

12 files changed

Lines changed: 727 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+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using BenchmarkDotNet.Attributes;
2+
using ModularityKit.Mutator.Governance.Abstractions.Queries.Model.Decisions;
3+
using ModularityKit.Mutator.Governance.Abstractions.Queries.Model.Requests;
4+
using ModularityKit.Mutator.Benchmarks.Governance.Queries.Support;
5+
6+
namespace ModularityKit.Mutator.Benchmarks.Governance.Queries;
7+
8+
/// <summary>
9+
/// Benchmarks governance query and read paths in the governance runtime.
10+
/// </summary>
11+
[BenchmarkCategory("Governance")]
12+
[MemoryDiagnoser]
13+
[InProcess]
14+
public class GovernanceQueryReadBenchmarks
15+
{
16+
private GovernanceQueryReadBenchmarkSupport.GovernanceQueryReadBenchmarkFixture _fixture = null!;
17+
18+
/// <summary>
19+
/// Prepares a seeded query store for the read benchmarks.
20+
/// </summary>
21+
[GlobalSetup]
22+
public void Setup()
23+
{
24+
_fixture = GovernanceQueryReadBenchmarkSupport.CreateFixture();
25+
}
26+
27+
/// <summary>
28+
/// Measures listing pending governance requests.
29+
/// </summary>
30+
[Benchmark(Baseline = true)]
31+
public async Task GetPendingRequests()
32+
{
33+
var result = await _fixture.RequestStore.GetPendingRequestsAsync(
34+
MutationRequestQueries.Pending()).ConfigureAwait(false);
35+
36+
GC.KeepAlive(result);
37+
}
38+
39+
/// <summary>
40+
/// Measures listing pending approval queue entries.
41+
/// </summary>
42+
[Benchmark]
43+
public async Task GetPendingApprovalQueue()
44+
{
45+
var result = await _fixture.ApprovalStore.GetPendingApprovalQueueAsync(
46+
MutationRequestQueries.PendingApprovalQueue()).ConfigureAwait(false);
47+
48+
GC.KeepAlive(result);
49+
}
50+
51+
/// <summary>
52+
/// Measures reading recent execution outcome decisions.
53+
/// </summary>
54+
[Benchmark]
55+
public async Task GetRecentExecutionOutcomes()
56+
{
57+
var result = await _fixture.DecisionStore.GetRecentDecisionsAsync(
58+
MutationRequestDecisionQuery.RecentExecutionOutcomes(),
59+
take: 64).ConfigureAwait(false);
60+
61+
GC.KeepAlive(result);
62+
}
63+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using ModularityKit.Mutator.Governance.Runtime.Storage;
2+
3+
namespace ModularityKit.Mutator.Benchmarks.Governance.Queries.Support;
4+
5+
/// <summary>
6+
/// Builds repeatable governance query benchmark fixtures.
7+
/// </summary>
8+
internal static class GovernanceQueryReadBenchmarkSupport
9+
{
10+
/// <summary>
11+
/// Creates a seeded fixture for query and read benchmarks.
12+
/// </summary>
13+
public static GovernanceQueryReadBenchmarkFixture CreateFixture()
14+
=> new(
15+
RequestStore: PendingRequestQueryBenchmarkSeed.CreateStore(),
16+
ApprovalStore: PendingApprovalQueryBenchmarkSeed.CreateStore(),
17+
DecisionStore: RecentDecisionQueryBenchmarkSeed.CreateStore());
18+
19+
/// <summary>
20+
/// Shared fixture for governance query and read scenarios.
21+
/// </summary>
22+
internal sealed record GovernanceQueryReadBenchmarkFixture(
23+
InMemoryMutationRequestStore RequestStore,
24+
InMemoryMutationRequestStore ApprovalStore,
25+
InMemoryMutationRequestStore DecisionStore);
26+
}

0 commit comments

Comments
 (0)