public async Task ShouldSplit() { var settings = new AggregateSettings() { MaxMutationCommits = 1 }; await using var context = new MongoDbContext <License>(settings); var aggregateId = "ShouldSplit"; var aggregate = await context.Repository.GetLatest(aggregateId); var now = context.DateTime.UtcNow; var createdOn = now; aggregate.Apply(new LicenseCreated() { CreatedOn = createdOn }); aggregate.State.ExpiresOn.ShouldBe(createdOn.AddDays(365)); await aggregate.Commit(); aggregate = await context.Repository.GetLatest(aggregateId); aggregate.Apply(new LicenseNoop()); aggregate.Apply(new LicenseReleased()); aggregate.State.Released.ShouldBe(true); await aggregate.Commit(); var collection = context.Database.GetCollection <Aggregate <License> >("License"); var query = await collection.FindAsync( x => x.AggregateId == aggregateId, new FindOptions <Aggregate <License> >() { Sort = Builders <Aggregate <License> > .Sort.Descending(x => x.AggregateVersion) } ); var record = await query.ToListAsync(); record[1].ShouldNotBeNull(); record[1].AggregateVersion.ShouldBe(0); record[1].TimeToLive.ShouldBe(now); record[0].ShouldNotBeNull(); record[0].TimeToLive.ShouldBeNull(); record[0].AggregateVersion.ShouldBe(1); record[0].MutationVersion.ShouldBe(2); record[0].MutationCommits.Count.ShouldBe(1); record[0].MutationCommits[0].MutationEvents.Count.ShouldBe(2); record[0].MutationCommits[0].MutationEvents[0].EventId.ShouldBe(1); record[0].MutationCommits[0].MutationEvents[0].Mutation.ShouldBeOfType <LicenseNoop>(); record[0].MutationCommits[0].MutationEvents[1].EventId.ShouldBe(2); record[0].MutationCommits[0].MutationEvents[1].Mutation.ShouldBeOfType <LicenseReleased>(); }
public MongoDbContext(AggregateSettings settings = default, bool destroy = true, IDateTime dateTime = default, string databaseId = default) { _databaseId = databaseId ?? "v" + Guid.NewGuid().ToString().Replace("-", ""); _dateTime = dateTime ?? new StaticDateTime(); _destroy = destroy; var connectionSettings = MongoClientSettings.FromConnectionString("mongodb://localhost:27017"); _client = new MongoClient(connectionSettings); _database = _client.GetDatabase(_databaseId); _factory = new AggregateCollectionFactory(_database); _settings = settings; _repository = new AggregateRepository <T>(_factory, _dateTime, _settings); }
public static async Task Main(string[] args) { var threads = 24; var aggregates = 10000; var iterations = int.MaxValue; var isRunning = true; //var iterations = 20000; var settings = new AggregateSettings() { MaxMutationCommits = 100 }; await using var context = new MongoDbContext <Counter>(settings, false, new DateTimeService(), "v2f66bb5f716d4743b2a8e80a568ac932"); var aggregateIds = Enumerable.Range(0, aggregates).Select(x => $"agg_{x:X8}").ToArray(); var totals = new int[aggregateIds.Length]; var mutations = 0; var concurrency = 0; for (var i = 0; i < aggregateIds.Length; i++) { var aggregate = await context.Repository.GetLatest(aggregateIds[i]); totals[i] = aggregate.State.Count; } var sw = Stopwatch.StartNew(); var tasks = Enumerable.Range(0, threads).Select(xx => Task.Run(async() => { var m = 0; var it = 0; var r = new Random((xx * 1024 + 1024)); var values = new int[aggregateIds.Length]; while (isRunning) { it++; var idx = r.Next(0, aggregateIds.Length); var c = r.Next(1, 5); var increments = new int[c]; for (var p = 0; p < increments.Length; p++) { increments[p] = r.Next(2, 6); values[idx] += increments[p]; m++; } var attempts = int.MaxValue; while (attempts-- > 0) { var aggregate = await context.Repository.GetLatest(aggregateIds[idx]); var amount = 0; var count = aggregate.State.Count; var start = count; for (var p = 0; p < increments.Length; p++) { count += increments[p]; amount += increments[p]; aggregate.Apply(new Increment() { Amount = increments[p] }); aggregate.State.Count.ShouldBe(count); } try { await aggregate.Commit(); break; } catch (ConcurrencyException) { await Task.Yield(); Interlocked.Increment(ref concurrency); continue; } } } for (var i = 0; i < aggregateIds.Length; i++) { Interlocked.Add(ref totals[i], values[i]); } Interlocked.Add(ref iterations, it); Interlocked.Add(ref mutations, m); })).ToArray(); while (!Console.KeyAvailable) { await Task.Delay(10); } isRunning = false; await Task.WhenAll(tasks); sw.Stop(); for (var i = 0; i < aggregateIds.Length; i++) { var aggregate = await context.Repository.GetLatest(aggregateIds[i]); aggregate.State.Count.ShouldBe(totals[i]); } Console.WriteLine($"Elapsed: {sw.Elapsed}, Avg: {(float)sw.ElapsedMilliseconds / iterations}ms, {(int)(iterations / sw.Elapsed.TotalSeconds)}/s, Concurrency Errors: {concurrency}, Mutations: {mutations}"); }
public async Task Concurrency() { var settings = new AggregateSettings() { MaxMutationCommits = 10 }; await using var context = new MongoDbContext <Counter>(settings); var aggregateIds = Enumerable.Range(0, 4).Select(x => Guid.NewGuid().ToString()).ToArray(); var totals = new int[aggregateIds.Length]; var concurrency = 0; var tasks = Enumerable.Range(0, 4).Select(xx => Task.Run(async() => { var r = new Random((xx * 1024 + 1024)); var cc = 0; var values = new int[aggregateIds.Length]; for (var i = 0; i < 50; i++) { var idx = r.Next(0, aggregateIds.Length); var c = r.Next(1, 5); var increments = new int[c]; for (var p = 0; p < increments.Length; p++) { increments[p] = r.Next(2, 6); values[idx] += increments[p]; } var attempts = int.MaxValue; while (attempts-- > 0) { var aggregate = await context.Repository.GetLatest(aggregateIds[idx]); var count = aggregate.State.Count; for (var p = 0; p < increments.Length; p++) { count += increments[p]; aggregate.Apply(new Increment() { Amount = increments[p] }); aggregate.State.Count.ShouldBe(count); } try { await aggregate.Commit(); break; } catch (ConcurrencyException) { await Task.Yield(); cc++; continue; } } } for (var i = 0; i < aggregateIds.Length; i++) { Interlocked.Add(ref totals[i], values[i]); } Interlocked.Add(ref concurrency, cc); })).ToArray(); await Task.WhenAll(tasks); for (var i = 0; i < aggregateIds.Length; i++) { var aggregate = await context.Repository.GetLatest(aggregateIds[i]); aggregate.State.Count.ShouldBe(totals[i]); } concurrency.ShouldBeGreaterThan(0); }