Skip to content

Commit fecaa46

Browse files
committed
Micro optimizations. Enhance performance investigation documentation and benchmarks for MongoDB persistence
- Updated Performance-Investigation.md to reflect current state and findings. - Added SnapshotAssistedRebuildBenchmarks to compare full and snapshot-assisted rebuilds. - Refined ReadFromStreamBenchmarks and StreamRevisionWindowBenchmarks for consistency. - Adjusted benchmark job configurations for improved clarity and performance. - Fixed minor issues in benchmark scripts and extension methods for better functionality.
1 parent 0c76129 commit fecaa46

8 files changed

Lines changed: 331 additions & 132 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ jobs:
8888
# The test project reads NEventStore.MongoDB from the environment and expects
8989
# a real MongoDB server. This sidecar provides that dependency for each matrix leg.
9090
mongodb:
91-
image: mongo:7.0
91+
image: mongo:8
9292
ports:
9393
- 27017:27017
9494
options: >-

docs/Performance-Investigation.md

Lines changed: 224 additions & 99 deletions
Large diffs are not rendered by default.

scripts/benchmark-snapshot.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ if ($LASTEXITCODE -ne 0)
7676
throw "Benchmark execution failed with exit code $LASTEXITCODE"
7777
}
7878

79-
$reportFiles = Get-ChildItem -Path $resultsDir -Filter '*-report-github.md' -File
79+
$reportFiles = @(Get-ChildItem -Path $resultsDir -Filter '*-report-github.md' -File)
8080
if (-not $reportFiles)
8181
{
8282
throw "No benchmark report files found in $resultsDir"

src/NEventStore.Persistence.MongoDB.Benchmark/Benchmarks/ReadFromStreamBenchmarks.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace NEventStore.Persistence.MongoDB.Benchmark.Benchmarks
66
{
77
[Config(typeof(AllowNonOptimized))]
8-
[SimpleJob(launchCount: 3, warmupCount: 3, iterationCount: 3, invocationCount: 1)]
8+
[SimpleJob(launchCount: 3, warmupCount: 3, iterationCount: 3)]
99
[MemoryDiagnoser]
1010
[MeanColumn, StdErrorColumn, StdDevColumn, MinColumn, MaxColumn, IterationsColumn]
1111
public class ReadFromStreamBenchmarks
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using BenchmarkDotNet.Attributes;
2+
using NEventStore.Persistence.MongoDB;
3+
using NEventStore.Persistence.MongoDB.Benchmark.Support;
4+
using System;
5+
6+
namespace NEventStore.Persistence.MongoDB.Benchmark.Benchmarks
7+
{
8+
/// <summary>
9+
/// Compares full aggregate rebuilds with snapshot-assisted rebuilds over the tail of a long stream.
10+
/// </summary>
11+
[Config(typeof(AllowNonOptimized))]
12+
[SimpleJob(launchCount: 3, warmupCount: 3, iterationCount: 3)]
13+
[MemoryDiagnoser]
14+
[MeanColumn, StdErrorColumn, StdDevColumn, MinColumn, MaxColumn, IterationsColumn]
15+
public class SnapshotAssistedRebuildBenchmarks
16+
{
17+
[Params(1000, 10000)]
18+
public int TotalCommitsInStream { get; set; }
19+
20+
[Params(10, 100, 1000)]
21+
public int CommitsAfterSnapshot { get; set; }
22+
23+
private readonly Guid _streamId = Guid.NewGuid();
24+
private IStoreEvents _eventStore = null!;
25+
private ISnapshot _snapshot = null!;
26+
27+
[GlobalSetup]
28+
public void Setup()
29+
{
30+
var options = new MongoPersistenceOptions
31+
{
32+
PersistStreamHeadsOnBackgroundThread = false
33+
};
34+
35+
_eventStore = EventStoreHelpers.WireupEventStore(options);
36+
_eventStore.Advanced.Purge();
37+
38+
using (var stream = _eventStore.CreateStream(_streamId))
39+
{
40+
for (int i = 1; i <= TotalCommitsInStream; i++)
41+
{
42+
stream.Add(new EventMessage { Body = new SomeDomainEvent { Value = i.ToString() } });
43+
stream.CommitChanges(Guid.NewGuid());
44+
}
45+
}
46+
47+
var snapshotRevision = Math.Max(1, TotalCommitsInStream - CommitsAfterSnapshot);
48+
_snapshot = new Snapshot(_streamId.ToString(), snapshotRevision, new SomeDomainEvent { Value = snapshotRevision.ToString() });
49+
_eventStore.Advanced.AddSnapshot(_snapshot);
50+
}
51+
52+
[Benchmark(Baseline = true)]
53+
public int FullRebuild()
54+
{
55+
using var stream = _eventStore.OpenStream(_streamId, 0, int.MaxValue);
56+
return stream.CommittedEvents.Count;
57+
}
58+
59+
[Benchmark]
60+
public int SnapshotAssistedRebuild()
61+
{
62+
using var stream = _eventStore.OpenStream(_snapshot, int.MaxValue);
63+
return stream.CommittedEvents.Count;
64+
}
65+
}
66+
}

src/NEventStore.Persistence.MongoDB.Benchmark/Benchmarks/StreamRevisionWindowBenchmarks.cs

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ namespace NEventStore.Persistence.MongoDB.Benchmark.Benchmarks
88
/// <summary>
99
/// Measures per-stream reads over focused revision windows.
1010
/// </summary>
11-
[Config(typeof(AllowNonOptimized))]
12-
[SimpleJob(launchCount: 3, warmupCount: 3, iterationCount: 3, invocationCount: 1)]
13-
[MemoryDiagnoser]
11+
[Config(typeof(AllowNonOptimized))]
12+
[SimpleJob(launchCount: 3, warmupCount: 3, iterationCount: 3)]
13+
[MemoryDiagnoser]
1414
[MeanColumn, StdErrorColumn, StdDevColumn, MinColumn, MaxColumn, IterationsColumn]
1515
public class StreamRevisionWindowBenchmarks
1616
{
@@ -20,35 +20,44 @@ public class StreamRevisionWindowBenchmarks
2020
[Params(10, 100, 1000)]
2121
public int RevisionWindowSize { get; set; }
2222

23-
private static readonly Guid StreamId = Guid.NewGuid();
24-
private readonly IStoreEvents _eventStore;
25-
private readonly IPersistStreams _persistence;
26-
27-
public StreamRevisionWindowBenchmarks()
28-
{
29-
_eventStore = EventStoreHelpers.WireupEventStore();
30-
_persistence = (IPersistStreams)_eventStore.Advanced;
31-
}
23+
private readonly string _streamId = Guid.NewGuid().ToString();
24+
private readonly IPersistStreams _persistence;
25+
26+
public StreamRevisionWindowBenchmarks()
27+
{
28+
_persistence = (IPersistStreams)EventStoreHelpers.WireupEventStore().Advanced;
29+
}
3230

3331
[GlobalSetup]
3432
public void Setup()
3533
{
3634
_persistence.Purge();
3735

38-
using var stream = _eventStore.CreateStream(StreamId);
39-
for (int i = 0; i < TotalCommitsInStream; i++)
40-
{
41-
stream.Add(new EventMessage { Body = new SomeDomainEvent { Value = i.ToString() } });
42-
stream.CommitChanges(Guid.NewGuid());
43-
}
44-
}
36+
for (int i = 1; i <= TotalCommitsInStream; i++)
37+
{
38+
var attempt = new CommitAttempt(
39+
bucketId: Bucket.Default,
40+
streamId: _streamId,
41+
streamRevision: i,
42+
commitId: Guid.NewGuid(),
43+
commitSequence: i,
44+
commitStamp: DateTime.UtcNow,
45+
headers: null,
46+
events:
47+
[
48+
new EventMessage { Body = new SomeDomainEvent { Value = i.ToString() } }
49+
]);
50+
51+
_persistence.Commit(attempt);
52+
}
53+
}
4554

4655
[Benchmark]
4756
public int ReadTailRevisionWindow()
4857
{
49-
var minRevision = Math.Max(1, TotalCommitsInStream - RevisionWindowSize + 1);
50-
var maxRevision = TotalCommitsInStream;
51-
return _persistence.GetFrom(Bucket.Default, StreamId.ToString(), minRevision, maxRevision).Count();
52-
}
53-
}
54-
}
58+
var minRevision = Math.Max(1, TotalCommitsInStream - RevisionWindowSize + 1);
59+
var maxRevision = TotalCommitsInStream;
60+
return _persistence.GetFrom(Bucket.Default, _streamId, minRevision, maxRevision).Count();
61+
}
62+
}
63+
}

src/NEventStore.Persistence.MongoDB/ExtensionMethods.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public static ICommit ToCommit(this BsonDocument doc, IDocumentSerializer serial
8585
{
8686
BsonValue payload = mc.Events[i][MongoCommitFields.Payload];
8787
events[i] = payload.IsBsonDocument
88-
? BsonSerializer.Deserialize<EventMessage>(payload.ToBsonDocument())!
88+
? BsonSerializer.Deserialize<EventMessage>(payload.AsBsonDocument)
8989
: serializer.Deserialize<EventMessage>(payload.AsByteArray)!; // ByteStreamDocumentSerializer ?!?! doesn't work this way!
9090
}
9191

src/NEventStore.Persistence.MongoDB/MongoPersistenceEngine.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
#pragma warning disable CA2254 // Template should be a static expression
33

44
using System.Globalization;
5+
using Microsoft.Extensions.Logging;
56
using MongoDB.Bson;
67
using MongoDB.Driver;
7-
using Microsoft.Extensions.Logging;
88
using NEventStore.Logging;
99
using NEventStore.Persistence.MongoDB.Support;
1010
using NEventStore.Serialization;
@@ -559,9 +559,8 @@ public virtual IEnumerable<IStreamHead> GetStreamsToSnapshot(string bucketId, in
559559
.Find(query)
560560
.Sort(SortByDescendingSnapshotRevision)
561561
.Limit(1)
562-
.ToEnumerable()
563-
.Select(mc => mc.ToSnapshot(_serializer))
564-
.FirstOrDefault();
562+
.FirstOrDefault()
563+
?.ToSnapshot(_serializer);
565564
});
566565
}
567566

0 commit comments

Comments
 (0)