public override async Task UpdateAsync(IReadOnlyCollection <ReadModelUpdate> readModelUpdates,
                                               IReadModelContextFactory readModelContextFactory,
                                               Func <IReadModelContext, IReadOnlyCollection <IDomainEvent>, ReadModelEnvelope <TReadModel>, CancellationToken,
                                                     Task <ReadModelUpdateResult <TReadModel> > > updateReadModel,
                                               CancellationToken cancellationToken)
        {
            using (var dbContext = _contextProvider.CreateContext())
            {
                foreach (var readModelUpdate in readModelUpdates)
                {
                    await _transientFaultHandler.TryAsync(
                        c => UpdateReadModelAsync(
                            // ReSharper disable once AccessToDisposedClosure
                            dbContext,
                            readModelContextFactory,
                            updateReadModel,
                            c,
                            readModelUpdate),
                        Label.Named("efcore-read-model-update"),
                        cancellationToken)
                    .ConfigureAwait(false);

                    cancellationToken.ThrowIfCancellationRequested();
                }
            }
        }
Exemplo n.º 2
0
        public async Task UpdateAsync(IReadOnlyCollection <ReadModelUpdate> readModelUpdates,
                                      IReadModelContextFactory readModelContextFactory,
                                      Func <IReadModelContext, IReadOnlyCollection <IDomainEvent>, ReadModelEnvelope <TReadModel>, CancellationToken,
                                            Task <ReadModelUpdateResult <TReadModel> > > updateReadModel,
                                      CancellationToken cancellationToken)
        {
            var readModelDescription = _readModelDescriptionProvider.GetReadModelDescription <TReadModel>();

            _log.Verbose(() =>
            {
                var readModelIds = readModelUpdates
                                   .Select(u => u.ReadModelId)
                                   .Distinct()
                                   .OrderBy(i => i)
                                   .ToList();
                return($"Updating read models of type '{typeof(TReadModel).PrettyPrint()}' with _ids '{string.Join(", ", readModelIds)}' in collection '{readModelDescription.RootCollectionName}'");
            });

            foreach (var readModelUpdate in readModelUpdates)
            {
                await _transientFaultHandler.TryAsync(
                    c => UpdateReadModelAsync(readModelDescription, readModelUpdate, readModelContextFactory, updateReadModel, c),
                    Label.Named("mongodb-read-model-update"),
                    cancellationToken)
                .ConfigureAwait(false);
            }
        }
Exemplo n.º 3
0
 public Task UpdateAsync(IReadOnlyCollection <ReadModelUpdate> readModelUpdates,
                         IReadModelContextFactory readModelContextFactory,
                         Func <IReadModelContext, IReadOnlyCollection <IDomainEvent>, ReadModelEnvelope <TReadModel>, CancellationToken,
                               Task <ReadModelUpdateResult <TReadModel> > > updateReadModel, CancellationToken cancellationToken)
 {
     return(_readStore.UpdateAsync(readModelUpdates, readModelContextFactory, updateReadModel,
                                   cancellationToken));
 }
Exemplo n.º 4
0
        private async Task UpdateReadModelAsync(ReadModelDescription readModelDescription,
                                                ReadModelUpdate readModelUpdate,
                                                IReadModelContextFactory readModelContextFactory,
                                                Func <IReadModelContext, IReadOnlyCollection <IDomainEvent>, ReadModelEnvelope <TReadModel>, CancellationToken,
                                                      Task <ReadModelUpdateResult <TReadModel> > > updateReadModel,
                                                CancellationToken cancellationToken)
        {
            var collection = _mongoDatabase.GetCollection <TReadModel>(readModelDescription.RootCollectionName.Value);
            var filter     = Builders <TReadModel> .Filter.Eq(readmodel => readmodel.Id, readModelUpdate.ReadModelId);

            var result = collection.Find(filter).FirstOrDefault();

            var isNew = result == null;

            var readModelEnvelope = !isNew
                ? ReadModelEnvelope <TReadModel> .With(readModelUpdate.ReadModelId, result)
                : ReadModelEnvelope <TReadModel> .Empty(readModelUpdate.ReadModelId);

            var readModelContext      = readModelContextFactory.Create(readModelUpdate.ReadModelId, isNew);
            var readModelUpdateResult =
                await updateReadModel(readModelContext, readModelUpdate.DomainEvents, readModelEnvelope,
                                      cancellationToken).ConfigureAwait(false);

            if (!readModelUpdateResult.IsModified)
            {
                return;
            }

            if (readModelContext.IsMarkedForDeletion)
            {
                await DeleteAsync(readModelUpdate.ReadModelId, cancellationToken);

                return;
            }

            readModelEnvelope = readModelUpdateResult.Envelope;
            var originalVersion = readModelEnvelope.ReadModel.Version;

            readModelEnvelope.ReadModel.Version = readModelEnvelope.Version;
            try
            {
                await collection.ReplaceOneAsync <TReadModel>(
                    x => x.Id == readModelUpdate.ReadModelId && x.Version == originalVersion,
                    readModelEnvelope.ReadModel,
                    new UpdateOptions()
                {
                    IsUpsert = true
                },
                    cancellationToken);
            }
            catch (MongoWriteException e)
            {
                throw new OptimisticConcurrencyException(
                          $"Read model '{readModelUpdate.ReadModelId}' updated by another",
                          e);
            }
        }
Exemplo n.º 5
0
 public override async Task UpdateAsync(IReadOnlyCollection <ReadModelUpdate> readModelUpdates,
                                        IReadModelContextFactory readModelContextFactory,
                                        Func <IReadModelContext, IReadOnlyCollection <IDomainEvent>, ReadModelEnvelope <TReadModel>, CancellationToken,
                                              Task <ReadModelUpdateResult <TReadModel> > > updateReadModel,
                                        CancellationToken cancellationToken)
 {
     foreach (var readModelUpdate in readModelUpdates)
     {
         await _transientFaultHandler.TryAsync(
             c => UpdateReadModelAsync(readModelContextFactory, updateReadModel, c, readModelUpdate),
             Label.Named($"mssql-read-model-update-{ReadModelNameLowerCase}"),
             cancellationToken)
         .ConfigureAwait(false);
     }
 }
Exemplo n.º 6
0
        public async Task UpdateAsync(IReadOnlyCollection <ReadModelUpdate> readModelUpdates,
                                      IReadModelContextFactory readModelContextFactory,
                                      Func <IReadModelContext, IReadOnlyCollection <IDomainEvent>, ReadModelEnvelope <TReadModel>, CancellationToken,
                                            Task <ReadModelUpdateResult <TReadModel> > > updateReadModel,
                                      CancellationToken cancellationToken)
        {
            var readModelDescription = _readModelDescriptionProvider.GetReadModelDescription <TReadModel>();

            foreach (var readModelUpdate in readModelUpdates)
            {
                await _transientFaultHandler.TryAsync(
                    c => UpdateReadModelAsync(readModelDescription, readModelUpdate, readModelContextFactory, updateReadModel, c),
                    Label.Named("elasticsearch-read-model-update"),
                    cancellationToken)
                .ConfigureAwait(false);
            }
        }
        public override async Task UpdateAsync(IReadOnlyCollection <ReadModelUpdate> readModelUpdates,
                                               IReadModelContextFactory readModelContextFactory,
                                               Func <IReadModelContext, IReadOnlyCollection <IDomainEvent>, ReadModelEnvelope <TReadModel>, CancellationToken, Task <ReadModelUpdateResult <TReadModel> > > updateReadModel,
                                               CancellationToken cancellationToken)
        {
            using (await _asyncLock.WaitAsync(cancellationToken).ConfigureAwait(false))
            {
                foreach (var readModelUpdate in readModelUpdates)
                {
                    var readModelId = readModelUpdate.ReadModelId;

                    var isNew = !_readModels.TryGetValue(readModelId, out var readModelEnvelope);

                    if (isNew)
                    {
                        readModelEnvelope = ReadModelEnvelope <TReadModel> .Empty(readModelId);
                    }

                    var readModelContext = readModelContextFactory.Create(readModelId, isNew);

                    var readModelUpdateResult = await updateReadModel(
                        readModelContext,
                        readModelUpdate.DomainEvents,
                        readModelEnvelope,
                        cancellationToken)
                                                .ConfigureAwait(false);

                    if (!readModelUpdateResult.IsModified)
                    {
                        return;
                    }

                    readModelEnvelope = readModelUpdateResult.Envelope;

                    if (readModelContext.IsMarkedForDeletion)
                    {
                        _readModels.Remove(readModelId);
                    }
                    else
                    {
                        _readModels[readModelId] = readModelEnvelope;
                    }
                }
            }
        }
Exemplo n.º 8
0
        private async Task UpdateReadModelAsync(ReadModelDescription readModelDescription, ReadModelUpdate readModelUpdate,
                                                IReadModelContextFactory readModelContextFactory,
                                                Func <IReadModelContext, IReadOnlyCollection <IDomainEvent>, ReadModelEnvelope <TReadModel>, CancellationToken,
                                                      Task <ReadModelUpdateResult <TReadModel> > > updateReadModel, CancellationToken cancellationToken)
        {
            var readModelContext  = readModelContextFactory.Create(readModelUpdate.ReadModelId, true);
            var collection        = _mongoDatabase.GetCollection <TReadModel>(readModelDescription.RootCollectionName.Value);
            var readModelEnvelope = ReadModelEnvelope <TReadModel> .Empty(readModelUpdate.ReadModelId);

            var modelUpdateResult =
                await updateReadModel(readModelContext, readModelUpdate.DomainEvents, readModelEnvelope,
                                      cancellationToken).ConfigureAwait(false);

            readModelEnvelope = modelUpdateResult.Envelope;
            readModelEnvelope.ReadModel._id =
                ObjectIdGenerator.Instance.GenerateId(collection, readModelEnvelope.ReadModel);
            await collection.InsertOneAsync(
                readModelEnvelope.ReadModel,
                new InsertOneOptions { BypassDocumentValidation = true },
                cancellationToken);
        }
Exemplo n.º 9
0
        private async Task UpdateReadModelAsync(ReadModelDescription readModelDescription,
                                                ReadModelUpdate readModelUpdate,
                                                IReadModelContextFactory readModelContextFactory,
                                                Func <IReadModelContext, IReadOnlyCollection <IDomainEvent>, ReadModelEnvelope <TReadModel>, CancellationToken, Task <ReadModelUpdateResult <TReadModel> > > updateReadModel,
                                                CancellationToken cancellationToken)
        {
            var collection = _mongoDatabase.GetCollection <TReadModel>(readModelDescription.RootCollectionName.Value);
            var filter     = Builders <TReadModel> .Filter.Eq(readmodel => readmodel._id, readModelUpdate.ReadModelId);

            var result = collection.Find(filter).FirstOrDefault();

            var isNew = result == null;

            var readModelEnvelope = !isNew
                    ? ReadModelEnvelope <TReadModel> .With(readModelUpdate.ReadModelId, result)
                    : ReadModelEnvelope <TReadModel> .Empty(readModelUpdate.ReadModelId);

            var readModelContext      = readModelContextFactory.Create(readModelUpdate.ReadModelId, isNew);
            var readModelUpdateResult = await updateReadModel(readModelContext, readModelUpdate.DomainEvents, readModelEnvelope, cancellationToken).ConfigureAwait(false);

            if (!readModelUpdateResult.IsModified)
            {
                return;
            }


            readModelEnvelope = readModelUpdateResult.Envelope;
            readModelEnvelope.ReadModel._version = readModelEnvelope.Version;

            await collection.ReplaceOneAsync <TReadModel>(
                x => x._id == readModelUpdate.ReadModelId,
                readModelEnvelope.ReadModel,
                new UpdateOptions()
            {
                IsUpsert = true
            },
                cancellationToken);
        }
Exemplo n.º 10
0
        public override async Task UpdateAsync(
            IReadOnlyCollection <ReadModelUpdate> readModelUpdates,
            IReadModelContextFactory readModelContextFactory,
            Func <IReadModelContext, IReadOnlyCollection <IDomainEvent>, ReadModelEnvelope <TReadModel>, CancellationToken, Task <ReadModelUpdateResult <TReadModel> > > updateReadModel,
            CancellationToken cancellationToken)
        {
            var readModelType = typeof(TReadModel);

            Log.Verbose(() =>
            {
                var readModelIds = readModelUpdates
                                   .Select(u => u.ReadModelId)
                                   .Distinct()
                                   .OrderBy(i => i)
                                   .ToList();

                return($"Updating read models of type '{typeof(TReadModel).PrettyPrint()}' with ids '{string.Join(", ", readModelIds)}' in stream '{readModelType}-<id>'");
            });

            foreach (var readModelUpdate in readModelUpdates)
            {
                await UpdateReadModelAsync(readModelContextFactory, updateReadModel, cancellationToken, readModelUpdate);
            }
        }
        private async Task UpdateReadModelAsync(TDbContext dbContext, IReadModelContextFactory readModelContextFactory,
                                                Func <IReadModelContext, IReadOnlyCollection <IDomainEvent>, ReadModelEnvelope <TReadModel>, CancellationToken, Task <ReadModelUpdateResult <TReadModel> > > updateReadModel,
                                                CancellationToken cancellationToken,
                                                ReadModelUpdate readModelUpdate)
        {
            var readModelId       = readModelUpdate.ReadModelId;
            var readModelEnvelope = await GetAsync(dbContext, readModelId, cancellationToken, true)
                                    .ConfigureAwait(false);

            var entity = readModelEnvelope.ReadModel;
            var isNew  = entity == null;

            if (entity == null)
            {
                entity = await _readModelFactory.CreateAsync(readModelId, cancellationToken).ConfigureAwait(false);

                readModelEnvelope = ReadModelEnvelope <TReadModel> .With(readModelId, entity);
            }

            var readModelContext = readModelContextFactory.Create(readModelId, isNew);
            var originalVersion  = readModelEnvelope.Version;
            var updateResult     = await updateReadModel(
                readModelContext,
                readModelUpdate.DomainEvents,
                readModelEnvelope,
                cancellationToken)
                                   .ConfigureAwait(false);

            if (!updateResult.IsModified)
            {
                return;
            }

            if (readModelContext.IsMarkedForDeletion)
            {
                await DeleteAsync(dbContext, readModelId, cancellationToken).ConfigureAwait(false);

                return;
            }

            readModelEnvelope = updateResult.Envelope;
            entity            = readModelEnvelope.ReadModel;

            var descriptor = GetDescriptor(dbContext);
            var entry      = isNew
                ? dbContext.Add(entity)
                : dbContext.Entry(entity);

            descriptor.SetId(entry, readModelId);
            descriptor.SetVersion(entry, originalVersion, readModelEnvelope.Version);
            try
            {
                await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
            }
            catch (DbUpdateConcurrencyException e)
            {
                var databaseValues = await entry.GetDatabaseValuesAsync(cancellationToken)
                                     .ConfigureAwait(false);

                entry.CurrentValues.SetValues(databaseValues);
                throw new OptimisticConcurrencyException(e.Message, e);
            }

            Log.Verbose(() => $"Updated Entity Framework read model {typeof(TReadModel).PrettyPrint()} with ID '{readModelId}' to version '{readModelEnvelope.Version}'");
        }
Exemplo n.º 12
0
        private async Task UpdateReadModelAsync(
            ReadModelDescription readModelDescription,
            ReadModelUpdate readModelUpdate,
            IReadModelContextFactory readModelContextFactory,
            Func <IReadModelContext, IReadOnlyCollection <IDomainEvent>, ReadModelEnvelope <TReadModel>, CancellationToken, Task <ReadModelUpdateResult <TReadModel> > > updateReadModel,
            CancellationToken cancellationToken)
        {
            var readModelId = readModelUpdate.ReadModelId;

            var response = await _elasticClient.GetAsync <TReadModel>(
                readModelId,
                d => d
                .RequestConfiguration(c => c
                                      .AllowedStatusCodes((int)HttpStatusCode.NotFound))
                .Index(readModelDescription.IndexName.Value),
                cancellationToken)
                           .ConfigureAwait(false);

            var isNew = !response.Found;

            var readModelEnvelope = isNew
                ? ReadModelEnvelope <TReadModel> .Empty(readModelId)
                : ReadModelEnvelope <TReadModel> .With(readModelUpdate.ReadModelId, response.Source, response.Version);

            var context = readModelContextFactory.Create(readModelId, isNew);

            var readModelUpdateResult = await updateReadModel(
                context,
                readModelUpdate.DomainEvents,
                readModelEnvelope,
                cancellationToken)
                                        .ConfigureAwait(false);

            if (!readModelUpdateResult.IsModified)
            {
                return;
            }

            readModelEnvelope = readModelUpdateResult.Envelope;
            if (context.IsMarkedForDeletion)
            {
                await DeleteAsync(readModelId, cancellationToken).ConfigureAwait(false);

                return;
            }

            try
            {
                await _elasticClient.IndexAsync(
                    readModelEnvelope.ReadModel,
                    d =>
                {
                    d = d
                        .RequestConfiguration(c => c)
                        .Id(readModelId)
                        .Index(readModelDescription.IndexName.Value);
                    d = isNew
                            ? d.OpType(OpType.Create)
                            : d.VersionType(VersionType.ExternalGte).Version(readModelEnvelope.Version.GetValueOrDefault());
                    return(d);
                },
                    cancellationToken)
                .ConfigureAwait(false);
            }
            catch (ElasticsearchClientException e)
                when(e.Response?.HttpStatusCode == (int)HttpStatusCode.Conflict)
                {
                    throw new OptimisticConcurrencyException(
                              $"Read model '{readModelId}' updated by another",
                              e);
                }
        }
Exemplo n.º 13
0
        private async Task UpdateReadModelAsync(
            IReadModelContextFactory readModelContextFactory,
            Func <IReadModelContext, IReadOnlyCollection <IDomainEvent>, ReadModelEnvelope <TReadModel>, CancellationToken, Task <ReadModelUpdateResult <TReadModel> > > updateReadModel,
            CancellationToken cancellationToken,
            ReadModelUpdate readModelUpdate)
        {
            IMssqlReadModel mssqlReadModel;

            var readModelId       = readModelUpdate.ReadModelId;
            var readModelEnvelope = await GetAsync(readModelId, cancellationToken).ConfigureAwait(false);

            var readModel = readModelEnvelope.ReadModel;
            var isNew     = readModel == null;

            if (readModel == null)
            {
                readModel = await _readModelFactory.CreateAsync(readModelId, cancellationToken)
                            .ConfigureAwait(false);

                mssqlReadModel = readModel as IMssqlReadModel;
                if (mssqlReadModel != null)
                {
                    mssqlReadModel.AggregateId = readModelId;
                    mssqlReadModel.CreateTime  = readModelUpdate.DomainEvents.First().Timestamp;
                }

                readModelEnvelope = ReadModelEnvelope <TReadModel> .With(readModelUpdate.ReadModelId, readModel);
            }

            var readModelContext = readModelContextFactory.Create(readModelId, isNew);

            var originalVersion       = readModelEnvelope.Version;
            var readModelUpdateResult = await updateReadModel(
                readModelContext,
                readModelUpdate.DomainEvents,
                readModelEnvelope,
                cancellationToken)
                                        .ConfigureAwait(false);

            if (!readModelUpdateResult.IsModified)
            {
                return;
            }

            readModelEnvelope = readModelUpdateResult.Envelope;
            if (readModelContext.IsMarkedForDeletion)
            {
                await DeleteAsync(readModelId, cancellationToken).ConfigureAwait(false);

                return;
            }

            mssqlReadModel = readModel as IMssqlReadModel;
            if (mssqlReadModel != null)
            {
                mssqlReadModel.UpdatedTime = DateTimeOffset.Now;
                mssqlReadModel.LastAggregateSequenceNumber = (int)readModelEnvelope.Version.GetValueOrDefault();
            }
            else
            {
                SetVersion(readModel, (int?)readModelEnvelope.Version);
            }

            var sql = isNew
                ? _readModelSqlGenerator.CreateInsertSql <TReadModel>()
                : _readModelSqlGenerator.CreateUpdateSql <TReadModel>();

            var dynamicParameters = new DynamicParameters(readModel);

            if (originalVersion.HasValue)
            {
                dynamicParameters.Add("_PREVIOUS_VERSION", (int)originalVersion.Value);
            }

            var rowsAffected = await _connection.ExecuteAsync(
                Label.Named("mssql-store-read-model", ReadModelNameLowerCase),
                cancellationToken,
                sql,
                dynamicParameters).ConfigureAwait(false);

            if (rowsAffected != 1)
            {
                throw new OptimisticConcurrencyException(
                          $"Read model '{readModelEnvelope.ReadModelId}' updated by another");
            }

            Log.Verbose(() => $"Updated MSSQL read model {typeof(TReadModel).PrettyPrint()} with ID '{readModelId}' to version '{readModelEnvelope.Version}'");
        }
Exemplo n.º 14
0
 public abstract Task UpdateAsync(IReadOnlyCollection <ReadModelUpdate> readModelUpdates,
                                  IReadModelContextFactory readModelContextFactory,
                                  Func <IReadModelContext, IReadOnlyCollection <IDomainEvent>, ReadModelEnvelope <TReadModel>, CancellationToken,
                                        Task <ReadModelUpdateResult <TReadModel> > > updateReadModel,
                                  CancellationToken cancellationToken);
Exemplo n.º 15
0
            public async Task UpdateAsync(IReadOnlyCollection <ReadModelUpdate> readModelUpdates, IReadModelContextFactory readModelContextFactory,
                                          Func <IReadModelContext, IReadOnlyCollection <IDomainEvent>, ReadModelEnvelope <InMemoryThingyReadModel>, CancellationToken, Task <ReadModelUpdateResult <InMemoryThingyReadModel> > > updateReadModel, CancellationToken cancellationToken)
            {
                await _inner.UpdateAsync(readModelUpdates, readModelContextFactory, updateReadModel, cancellationToken);

                await UpdateCompletionSource.Task;
            }
Exemplo n.º 16
0
        private async Task UpdateReadModelAsync(
            IReadModelContextFactory readModelContextFactory,
            Func <IReadModelContext, IReadOnlyCollection <IDomainEvent>, ReadModelEnvelope <TReadModel>, CancellationToken, Task <ReadModelUpdateResult <TReadModel> > > updateReadModel,
            CancellationToken cancellationToken,
            ReadModelUpdate readModelUpdate)
        {
            var readModelId       = readModelUpdate.ReadModelId;
            var readModelEnvelope = await GetAsync(readModelId, cancellationToken).ConfigureAwait(false);

            var readModel = readModelEnvelope.ReadModel;
            var isNew     = readModel == null;

            if (readModel == null)
            {
                readModel = await _readModelFactory.CreateAsync(readModelId, cancellationToken).ConfigureAwait(false);

                readModelEnvelope = ReadModelEnvelope <TReadModel> .With(readModelUpdate.ReadModelId, readModel);
            }

            var readModelContext = readModelContextFactory.Create(readModelId, isNew);

            var originalVersion = readModelEnvelope.Version;

            var readModelUpdateResult = await updateReadModel(
                readModelContext,
                readModelUpdate.DomainEvents,
                readModelEnvelope,
                cancellationToken)
                                        .ConfigureAwait(false);

            if (!readModelUpdateResult.IsModified)
            {
                return;
            }

            readModelEnvelope = readModelUpdateResult.Envelope;

            if (readModelContext.IsMarkedForDeletion)
            {
                await DeleteAsync(readModelId, cancellationToken).ConfigureAwait(false);

                return;
            }

            var readModelType = typeof(TReadModel);

            var stream = readModelId == "null"
               ? readModelType.Name.ToLowerInvariant()
               : $"{readModelType.Name.ToLowerInvariant()}-{readModelId}";

            var messageInput = new MessageInput
            {
                ID    = Guid.NewGuid().ToString(),
                Type  = $"{readModelType.Name}.{readModelEnvelope.Version}",
                Value = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(readModelEnvelope.ReadModel))
            };

            try
            {
                if (readModelUpdate is CursorBasedReadModelUpdate cursorBasedReadModelUpdate)
                {
                    var cursorsMessageInput = new MessageInput
                    {
                        ID    = Guid.NewGuid().ToString(),
                        Type  = "cursors",
                        Value = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(cursorBasedReadModelUpdate.Cursors))
                    };

                    await _client.DB().AppendStreams(
                        new StreamInput(cursorBasedReadModelUpdate.CursorsStream, new List <MessageInput> {
                        cursorsMessageInput
                    }),
                        new StreamInput(stream, ConcurrencyCheck.ExpectStreamVersion(originalVersion.GetValueOrDefault()), new List <MessageInput> {
                        messageInput
                    })
                        ).ConfigureAwait(false);
                }
                else
                {
                    await _client.DB().AppendStream(stream, ConcurrencyCheck.ExpectStreamVersion(originalVersion.GetValueOrDefault()), messageInput).ConfigureAwait(false);
                }
            }
            catch (Exception e)
            {
                throw new OptimisticConcurrencyException($"Read model '{readModelEnvelope.ReadModelId}' updated by another", e);
            }

            Log.Verbose(() => $"Updated StreamsDB read model {typeof(TReadModel).PrettyPrint()} with ID '{readModelId}' to version '{readModelEnvelope.Version}'");
        }