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>();
        }
Example #2
0
        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);
        }
Example #3
0
        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);
        }