public async Task <AppendResult> SaveAsync <T>(T domainObject) where T : class, IDomainObject
        {
            AppendResult result = await SaveAsync(domainObject, false);

            AssertSave(domainObject, result);
            return(result);
        }
        private async Task <AppendResult> CreateEmptyStream(
            StreamIdInfo streamId,
            int expectedVersion,
            CancellationToken cancellationToken)
        {
            var appendResult = new AppendResult(StreamVersion.End, Position.End);

            using (var connection = await OpenConnection(cancellationToken))
                using (var transaction = await connection
                                         .BeginTransactionAsync(cancellationToken)
                                         .ConfigureAwait(false))
                    using (var command = BuildStoredProcedureCall(
                               _schema.CreateEmptyStream,
                               transaction,
                               Parameters.StreamId(streamId.MySqlStreamId),
                               Parameters.StreamIdOriginal(streamId.MySqlStreamId),
                               Parameters.MetadataStreamId(streamId.MetadataMySqlStreamId),
                               Parameters.ExpectedVersion(expectedVersion)))
                        using (var reader = await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false))
                        {
                            await reader.ReadAsync(cancellationToken).ConfigureAwait(false);

                            appendResult = new AppendResult(reader.GetInt32(0), reader.GetInt64(1));

                            reader.Close();

                            await transaction.CommitAsync(cancellationToken).ConfigureAwait(false);
                        }

            return(appendResult);
        }
示例#3
0
        public async Task InvokeAndSaveAsync(Type domainObjectType, ICommand cmd,
                                             IEnumerable <CommandMethodMapping> commandMapping)
        {
            Precondition.For(domainObjectType, nameof(domainObjectType)).NotNull("Type has to be set!");
            Precondition.For(cmd, nameof(cmd)).IsValidCommand();
            Precondition.For(commandMapping, nameof(commandMapping)).NotNull();

            var type = cmd.GetType();

            logger.LogTrace("Invoking Command \"{type}\" for \"{domainObjectType}\"", domainObjectType);
            var currentTry = 0;

            while (currentTry < retryCount)
            {
                if (currentTry > 0)
                {
                    logger.LogTrace("Retrying Command \"{type}\" for \"{domainObjectType}\"...");
                }
                AppendResult result = await InvokeAndSaveInternalAsync(domainObjectType, cmd, commandMapping);

                if (result.HadWrongVersion)
                {
                    currentTry++;
                }
                else
                {
                    break;
                }
            }
        }
示例#4
0
        private async Task <AppendResult> InsertEvent(EventCommit commit, bool versionCheck)
        {
            IClientSessionHandle session = null;

            if (UseTransactions)
            {
                session = await Database.Client.StartSessionAsync(new ClientSessionOptions());

                session.StartTransaction(new TransactionOptions());
            }

            var watch = Stopwatch.StartNew();

            commit.Ordinal = await identifier.Next("commit");

            AppendResult result = AppendResult.NoUpdate;

            try
            {
                var currentVersion = await GetVersion(commit.AggregateType, commit.AggregateId);

                if (versionCheck && currentVersion != commit.ExpectedPreviousVersion)
                {
                    logger.LogWarning("Event Version check requested and version was wrong . was: {0} expected: {1}",
                                      currentVersion, commit.ExpectedPreviousVersion);
                    //TODO If version check throw exception!
                    result = AppendResult.WrongVersion(commit.VersionCommit);
                }

                commit.VersionCommit = currentVersion + commit.Events.Count;

                await Collection.InsertOneAsync(commit);

                result = new AppendResult(commit.Id.ToString(), false, commit.VersionCommit, "SUCCESS");
            }
            catch (MongoWriteException e)
            {
                if (e.Message.Contains("E11000 duplicate key"))
                {
                    result = AppendResult.WrongVersion(commit.VersionCommit);
                }
                else
                {
                    logger.LogError(e, "Error when saving a commit for {type} {id}", commit.AggregateType,
                                    commit.AggregateId);
                    throw;
                }
            }
            finally
            {
                await CommitOrAbortTx(session, result);

                watch.Stop();
                logger.LogDebug("{Count} events for {Type} - {Id} handled. Result: {Result}.", commit.Events.Count,
                                commit.AggregateType, commit.AggregateId, result.CommitId);
            }

            return(result);
        }
示例#5
0
        public void AcceptedWhenNoError()
        {
            var result = new AppendResult("1", false, 14, "SUCCESS");

            HttpStatusCode code = StatusCodeConverter.From(result);

            Assert.Equal(HttpStatusCode.Accepted, code);
        }
示例#6
0
        public void ConflictWhenVersionError()
        {
            var result = new AppendResult("", true, 14, "WRONGVERSION");

            HttpStatusCode code = StatusCodeConverter.From(result);

            Assert.Equal(HttpStatusCode.Conflict, code);
        }
示例#7
0
        public async Task <AppendResult> SaveAsync <T>(T domainObject) where T : class, IDomainObject
        {
            AppendResult result = await SaveAsync(domainObject, false);

            if (result.HadWrongVersion)
            {
                throw new VersionConflictException(domainObject.GetType().Name, domainObject.Id,
                                                   result.CurrentVersion);
            }
            return(result);
        }
示例#8
0
        protected override async Task <AppendResult> SaveUncomittedEventsAsync <T>(T domainObject, bool versionCheck)
        {
            string streamName = context.StreamNamer.Resolve(domainObject);

            long expectedVersion = versionCheck ? ExpectedVersionFromDo(domainObject) : ExpectedVersion.Any;

            AppendResult result = await context.Writer.AppendAsync(streamName, domainObject.GetUncommittedEvents(),
                                                                   expectedVersion);

            return(result);
        }
示例#9
0
        public void ItThrowsAtEndOfRetries()
        {
            var repo = A.Fake <IDomainObjectRepository>();

            A.CallTo(() => repo.SaveAsync(A <IDomainObject> .Ignored, A <bool> .Ignored))
            .Returns(AppendResult.WrongVersion(2));

            ConventionCommandInvoker sut = GetSut(repo);

            var cmd = new SampleCommand();
        }
        private async Task <AppendResult> AppendMessagesToStream(
            StreamIdInfo streamId,
            int expectedVersion,
            NewStreamMessage[] messages,
            CancellationToken cancellationToken)
        {
            var appendResult        = new AppendResult(StreamVersion.End, Position.End);
            var nextExpectedVersion = expectedVersion;

            using (var connection = await OpenConnection(cancellationToken))
                using (var transaction = await connection
                                         .BeginTransactionAsync(cancellationToken)
                                         .ConfigureAwait(false))
                {
                    var throwIfAdditionalMessages = false;

                    for (var i = 0; i < messages.Length; i++)
                    {
                        bool messageExists;
                        (nextExpectedVersion, appendResult, messageExists) = await AppendMessageToStream(
                            streamId,
                            nextExpectedVersion,
                            messages[i],
                            transaction,
                            cancellationToken);

                        if (i == 0)
                        {
                            throwIfAdditionalMessages = messageExists;
                        }
                        else
                        {
                            if (throwIfAdditionalMessages && !messageExists)
                            {
                                throw new WrongExpectedVersionException(
                                          ErrorMessages.AppendFailedWrongExpectedVersion(
                                              streamId.MySqlStreamId.IdOriginal,
                                              expectedVersion),
                                          streamId.MySqlStreamId.IdOriginal,
                                          expectedVersion);
                            }
                        }
                    }

                    await transaction.CommitAsync(cancellationToken).NotOnCapturedContext();
                }

            await TryScavenge(streamId, cancellationToken).NotOnCapturedContext();

            return(appendResult);
        }
        public static PostMessagesResponse FromAppendResult(AppendResult r)
        {
            var array = new long[r.Ids.Count];

            for (int i = 0; i < array.Length; i++)
            {
                array[i] = r.Ids[i].GetOffset();
            }
            return(new PostMessagesResponse()
            {
                Position = r.Position,
                Offsets = array
            });
        }
示例#12
0
        private void AssertSave <T>(T domainObject, AppendResult result) where T : class, IDomainObject
        {
            if (result.HadWrongVersion)
            {
                Type   type = domainObject.GetType();
                string id   = domainObject.Id;

                logger.LogError("Version conflict when saving a {type} with id {id}. Version {CurrentVersion}", type,
                                id,
                                result.CurrentVersion);

                throw new VersionConflictException(domainObject.GetType().Name, domainObject.Id,
                                                   result.CurrentVersion);
            }
        }
示例#13
0
        private static async Task CommitOrAbortTx(IClientSessionHandle session, AppendResult result)
        {
            if (session != null)
            {
                if (result.HadWrongVersion || string.IsNullOrWhiteSpace(result.CommitId) || result.CurrentVersion <= 0)
                {
                    await session.AbortTransactionAsync();
                }
                else
                {
                    await session.CommitTransactionAsync();
                }

                session.Dispose();
            }
        }
        public async Task <AppendResult> SaveAsync <T>(T domainObject, bool preventVersionCheck)
            where T : class, IDomainObject
        {
            Precondition.For(domainObject, nameof(domainObject))
            .NotNull("The domainObject to be saved must not be null!");

            string type = typeof(T).FullName;

            if (!domainObject.HasUncommittedEvents)
            {
                string id = domainObject.Id;
                logger.LogDebug("\"{type}\"-{id} had no events to save", type, id);

                return(AppendResult.NoUpdate);
            }

            int count = domainObject.GetUncommittedEvents().Count;

            logger.LogTrace("Saving \"{type}\" with {count} events...", type, count);
            Stopwatch watch = Stopwatch.StartNew();

            bool         check  = domainObject.CheckVersionOnSave && !preventVersionCheck;
            AppendResult result = await SaveUncomittedEventsAsync(domainObject, check);

            if (!result.HadWrongVersion)
            {
                foreach (IEvent @event in domainObject.GetUncommittedEvents())
                {
                    if (configuration.PostSavePipeline != null)
                    {
                        configuration.PostSavePipeline(@event);
                    }

                    if (configuration.DirectDenormalizers != null)
                    {
                        await configuration.DirectDenormalizers.HandleAsync(@event);
                    }
                }
            }

            domainObject.CommitChanges(result.CurrentVersion);
            watch.Stop();

            logger.LogTrace("Saved {count} events for \"{type}\" in {watch.ElapsedMilliseconds}ms", count, type,
                            watch.ElapsedMilliseconds);
            return(result);
        }
示例#15
0
        public AppendResult Append(IMessageView sm)
        {
            AppendResult result = new AppendResult(false);
            bool         bottom = GetBottommostIndex() == Collection.Count - 2; // -2 because we already incremented Count

            Collection.Add(sm, sm.Model.Author == null);
            if (bottom)
            {
                UpdateLayout();
                ScrollToBottom();
                if (ActivationState != CoreWindowActivationState.Deactivated)
                {
                    result = new AppendResult(true);
                }
            }
            return(result);
        }
        public AppendResult Append(SignalMessageContainer sm)
        {
            AppendResult result = null;
            bool         bottom = GetBottommostIndex() == Collection.Count - 2; // -2 because we already incremented Count

            Collection.Add(sm, sm.Message.Author == null);
            if (bottom)
            {
                UpdateLayout();
                ScrollToBottom();
                if (ActivationState != CoreWindowActivationState.Deactivated)
                {
                    result = new AppendResult(sm.Index);
                }
            }
            return(result);
        }
示例#17
0
        private async Task <AppendResult> InsertEvent(EventCommit commit, bool versionCheck)
        {
            commit.Ordinal = await identifier.Next("commit");

            try
            {
                await Collection.InsertOneAsync(commit);
            }
            catch (MongoWriteException e)
            {
                if (e.Message.Contains("E11000 duplicate key "))
                {
                    return(AppendResult.WrongVersion(commit.VersionCommit));
                }

                throw;
            }
            return(new AppendResult(false, commit.VersionCommit));
        }
        public async Task <long> PersistFacts(Fact[] facts)
        {
            var factsByAggregate = facts.GroupBy(x => x.Identifier);

            AppendResult result = null;

            foreach (var aggregateWithEvents in factsByAggregate)
            {
                result = await _streamStore.AppendToStream(
                    aggregateWithEvents.Key,
                    ExpectedVersion.Any,
                    aggregateWithEvents
                    .Select(e => new NewStreamMessage(
                                Guid.NewGuid(),
                                _eventMapping.GetEventName(e.Event.GetType()),
                                _eventSerializer.SerializeObject(e.Event))).ToArray());
            }

            return(result?.CurrentPosition ?? await _streamStore.ReadHeadPosition());
        }
示例#19
0
        public async Task <AppendResult> SaveAsync <T>(T domainObject, bool preventVersionCheck)
            where T : class, IDomainObject
        {
            string type = typeof(T).FullName;

            if (!domainObject.HasUncommittedEvents)
            {
                Trace.WriteLine("No events to save");
                return(AppendResult.NoUpdate);
            }
            Trace.WriteLine($"Saving \"{type}\"...", TraceCategory);
            Stopwatch watch = Stopwatch.StartNew();

            bool         check  = domainObject.CheckVersionOnSave && !preventVersionCheck;
            AppendResult result = await SaveUncomittedEventsAsync(domainObject, check);

            domainObject.CommitChanges(result.CurrentVersion);
            watch.Stop();

            Trace.WriteLine($"Saved \"{type}\" in {watch.ElapsedMilliseconds}ms", TraceCategory);
            return(result);
        }
示例#20
0
        public async Task InvokeAndSaveAsync(Type domainObjectType, ICommand cmd,
                                             IEnumerable <CommandMethodMapping> commandMapping)
        {
            Precondition.For(domainObjectType, nameof(domainObjectType)).NotNull("Type has to be set!");
            Precondition.For(cmd, nameof(cmd)).IsValidCommand();
            Precondition.For(commandMapping, nameof(commandMapping)).NotNull();

            logger.LogTrace("Invoking Command \"{type}\" for \"{domainObjectType}\"", domainObjectType);
            var          currentTry = 0;
            AppendResult result     = AppendResult.NoUpdate;

            while (currentTry < retryCount)
            {
                if (currentTry > 0)
                {
                    logger.LogTrace("Retrying Command \"{type}\" for \"{domainObjectType}\"...");
                }

                result = await InvokeAndSaveInternalAsync(domainObjectType, cmd, commandMapping);

                if (result.HadWrongVersion)
                {
                    currentTry++;
                }
                else
                {
                    break;
                }
            }

            if (currentTry >= retryCount)
            {
                var msg =
                    $"Saving \"{domainObjectType.Name}\" with id \"{cmd.DomainObjectId}\" failed due to version conflicts";
                logger.LogWarning(msg);
                throw new VersionConflictException(domainObjectType.Name, cmd.DomainObjectId, result.CurrentVersion);
            }
        }
        public async Task InvokeAndSaveAsync(Type domainObjectType, ICommand cmd,
                                             IEnumerable <CommandMethodMapping> commands)
        {
            Precondition.For(cmd, nameof(cmd)).IsValidCommand();
            Precondition.For(domainObjectType, nameof(domainObjectType)).NotNull("Type has to be set!");


            var currentTry = 0;

            while (currentTry < retryCount)
            {
                AppendResult result = await InvokeAndSaveInternalAsync(domainObjectType, cmd, commands);

                if (result.HadWrongVersion)
                {
                    currentTry++;
                }
                else
                {
                    break;
                }
            }
        }
示例#22
0
        public Producer(int id, ProducerAppendBehavior behavior, IStreamStore store, IScheduler scheduler)
        {
            Id        = id;
            Behavior  = behavior;
            Store     = store ?? throw new ArgumentNullException(nameof(store));
            Scheduler = scheduler ?? throw new ArgumentNullException(nameof(scheduler));
            MessagePumpCancellation = new CancellationTokenSource();
            Mailbox = new BufferBlock <object>(new DataflowBlockOptions
            {
                BoundedCapacity    = int.MaxValue,
                MaxMessagesPerTask = 1,
                CancellationToken  = MessagePumpCancellation.Token
            });
            MessagePump = Task.Run(async() =>
            {
                try
                {
                    var text   = new Lorem();
                    var random = new Random();
                    while (!MessagePumpCancellation.Token.IsCancellationRequested)
                    {
                        var message = await Mailbox.ReceiveAsync(MessagePumpCancellation.Token).ConfigureAwait(false);
                        switch (message)
                        {
                        case AppendToStream append:
                            var messages = Enumerable
                                           .Range(0, random.Next(1, 100))                                                                            // produce between 1 and 99 messages per append
                                           .Select(index => new NewStreamMessage(Guid.NewGuid(), append.Stream, text.Sentences(random.Next(5, 10)))) //randomize the data a bit
                                           .ToArray();
                            if (behavior == ProducerAppendBehavior.Batched)
                            {
                                try
                                {
                                    var result = await Store
                                                 .AppendToStream(
                                        append.Stream, append.ExpectedVersion,
                                        messages,
                                        MessagePumpCancellation.Token)
                                                 .ConfigureAwait(false);

                                    await scheduler
                                    .ScheduleTellOnceAsync(
                                        () => Mailbox.Post(new AppendToStream {
                                        Stream = append.Stream, ExpectedVersion = result.CurrentVersion
                                    }),
                                        TimeSpan.FromMilliseconds(random.Next(100, 5000)),         // produce another append on the same stream in about 100 to 5000ms
                                        MessagePumpCancellation.Token)
                                    .ConfigureAwait(false);
                                }
                                catch (Exception exception)
                                {
                                    if (!MessagePumpCancellation.IsCancellationRequested)
                                    {
                                        Console.WriteLine("[{0}]AppendToStream failed because {1}", Id, exception);
                                        await scheduler
                                        .ScheduleTellOnceAsync(
                                            () => Mailbox.Post(new AppendToStream {
                                            Stream = append.Stream, ExpectedVersion = append.ExpectedVersion
                                        }),
                                            TimeSpan.FromMilliseconds(random.Next(100, 5000)),         // produce another append on the same stream in about 100 to 5000ms
                                            MessagePumpCancellation.Token)
                                        .ConfigureAwait(false);
                                    }
                                }
                            }
                            else
                            {
                                AppendResult result = new AppendResult(append.ExpectedVersion, -1);
                                try
                                {
                                    foreach (var msg in messages)
                                    {
                                        result = await Store
                                                 .AppendToStream(
                                            append.Stream, append.ExpectedVersion,
                                            messages,
                                            MessagePumpCancellation.Token)
                                                 .ConfigureAwait(false);
                                    }

                                    await scheduler
                                    .ScheduleTellOnceAsync(
                                        () => Mailbox.Post(new AppendToStream {
                                        Stream = append.Stream, ExpectedVersion = result.CurrentVersion
                                    }),
                                        TimeSpan.FromMilliseconds(random.Next(100, 5000)),         // produce another append on the same stream in about 100 to 5000ms
                                        MessagePumpCancellation.Token)
                                    .ConfigureAwait(false);
                                }
                                catch (Exception exception)
                                {
                                    if (!MessagePumpCancellation.IsCancellationRequested)
                                    {
                                        Console.WriteLine("[{0}]AppendToStream failed because {1}", Id, exception);
                                        await scheduler
                                        .ScheduleTellOnceAsync(
                                            () => Mailbox.Post(new AppendToStream {
                                            Stream = append.Stream, ExpectedVersion = result.CurrentVersion
                                        }),
                                            TimeSpan.FromMilliseconds(random.Next(100, 5000)),         // produce another append on the same stream in about 100 to 5000ms
                                            MessagePumpCancellation.Token)
                                        .ConfigureAwait(false);
                                    }
                                }
                            }
                            break;
                        }
                    }
                }
                catch (OperationCanceledException) { }
            }, MessagePumpCancellation.Token);
        }
示例#23
0
 protected override Task <List <IEvent> > ByAppendResult(AppendResult result)
 {
     throw new NotImplementedException();
 }
 public static LogRef ExpectOK(this AppendResult result)
 => result.Success ? (LogRef)result : throw new Exception("Append failed!");
示例#25
0
 protected abstract Task <List <IEvent> > ByAppendResult(AppendResult result);
        protected override async Task <AppendResult> AppendToStreamInternal(
            string streamId,
            int expectedVersion,
            NewStreamMessage[] messages,
            CancellationToken cancellationToken)
        {
            int       maxRetries = 2; //TODO too much? too little? configurable?
            Exception exception;

            int retryCount = 0;

            do
            {
                try
                {
                    AppendResult result;
                    var          streamIdInfo = new StreamIdInfo(streamId);

                    using (var connection = await OpenConnection(cancellationToken))
                        using (var transaction = connection.BeginTransaction())
                            using (var command = BuildFunctionCommand(
                                       _schema.AppendToStream,
                                       transaction,
                                       Parameters.StreamId(streamIdInfo.PostgresqlStreamId),
                                       Parameters.StreamIdOriginal(streamIdInfo.PostgresqlStreamId),
                                       Parameters.MetadataStreamId(streamIdInfo.MetadataPosgresqlStreamId),
                                       Parameters.ExpectedVersion(expectedVersion),
                                       Parameters.CreatedUtc(_settings.GetUtcNow?.Invoke()),
                                       Parameters.NewStreamMessages(messages)))
                            {
                                try
                                {
                                    using (var reader =
                                               await command.ExecuteReaderAsync(cancellationToken).NotOnCapturedContext())
                                    {
                                        await reader.ReadAsync(cancellationToken).NotOnCapturedContext();

                                        result = new AppendResult(reader.GetInt32(0), reader.GetInt64(1));
                                    }

                                    await transaction.CommitAsync(cancellationToken).NotOnCapturedContext();
                                }
                                catch (PostgresException ex) when(ex.IsWrongExpectedVersion())
                                {
                                    await transaction.RollbackAsync(cancellationToken).NotOnCapturedContext();

                                    throw new WrongExpectedVersionException(
                                              ErrorMessages.AppendFailedWrongExpectedVersion(
                                                  streamIdInfo.PostgresqlStreamId.IdOriginal,
                                                  expectedVersion),
                                              ex);
                                }
                            }

                    if (_settings.ScavengeAsynchronously)
                    {
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
                        Task.Run(() => TryScavenge(streamIdInfo, cancellationToken), cancellationToken);
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
                    }
                    else
                    {
                        await TryScavenge(streamIdInfo, cancellationToken).NotOnCapturedContext();
                    }

                    return(result);
                }
                catch (PostgresException ex) when(ex.IsDeadlock())
                {
                    exception = ex;
                    retryCount++;
                }
            } while(retryCount < maxRetries);

            ExceptionDispatchInfo.Capture(exception).Throw();
            return(default); // never actually run
        protected void initSuite2()
        {
            // some tests require time-stamps, other don't. The ones that do use this method, others don't.
            createTS = createNewTS();
            createTime = Suite.ADSclient.GetCurrentServerTime();// DateTime.Now;      // changesSince = full TS
            //createTime = createTime.AddHours(Suite.locUTCOff_hrs - Suite.servUTCOff_hrs); // Sets createTime to the location's timezone

            appendResult = appendData2(ptArray1);

            Thread.Sleep(2000);

            halfTime = Suite.ADSclient.GetCurrentServerTime(); //DateTime.Now;        // asAt = 1st half TS; changesSince = 2nd half TS
            //halfTime = halfTime.AddHours(Suite.locUTCOff_hrs - Suite.servUTCOff_hrs); // Sets halfTime to the location's timezone
        }
示例#28
0
        public void ItAppliesTheVersion()
        {
            AppendResult result = GetSut(15);

            Assert.Equal(15, result.CurrentVersion);
        }
示例#29
0
 public static HttpStatusCode From(AppendResult result)
 {
     return(result.HadWrongVersion ? HttpStatusCode.Conflict : HttpStatusCode.Accepted);
 }