public async Task Should_clear_dispatched_messages_after_given_expiry() { var storage = new InMemoryOutboxStorage(); var messageId = "myId"; var beforeStore = DateTime.UtcNow; var messageToStore = new OutboxMessage(messageId, new[] { new TransportOperation("x", null, null, null) }); using (var transaction = await storage.BeginTransaction(new ContextBag())) { await storage.Store(messageToStore, transaction, new ContextBag()); await transaction.Commit(); } // Account for the low resolution of DateTime.UtcNow. var afterStore = DateTime.UtcNow.AddTicks(1); await storage.SetAsDispatched(messageId, new ContextBag()); storage.RemoveEntriesOlderThan(beforeStore); var message = await storage.Get(messageId, new ContextBag()); Assert.NotNull(message); storage.RemoveEntriesOlderThan(afterStore); message = await storage.Get(messageId, new ContextBag()); Assert.Null(message); }
public bool TryGet(string messageId, out OutboxMessage message) { message = null; if (ExistingMessage != null && ExistingMessage.MessageId == messageId) { message = ExistingMessage; return true; } return false; }
public Task Store(OutboxMessage message, OutboxTransaction transaction, ContextBag context) { var tx = (InMemoryOutboxTransaction) transaction; tx.Enlist(() => { if (!storage.TryAdd(message.MessageId, new StoredMessage(message.MessageId, message.TransportOperations))) { throw new Exception($"Outbox message with id '{message.MessageId}' is already present in storage."); } }); return TaskEx.CompletedTask; }
public async Task DispatchEventsAsync() { var domainEvents = _domainEventsProvider.GetAllDomainEvents(); var domainEventNotifications = new List <IDomainEventNotification <IDomainEvent> >(); foreach (var domainEvent in domainEvents) { Type domainEvenNotificationType = typeof(IDomainEventNotification <>); var domainNotificationWithGenericType = domainEvenNotificationType.MakeGenericType(domainEvent.GetType()); var domainNotification = _scope.ResolveOptional(domainNotificationWithGenericType, new List <Parameter> { new NamedParameter("domainEvent", domainEvent) }); if (domainNotification != null) { domainEventNotifications.Add(domainNotification as SeedWork.IDomainEventNotification <IDomainEvent>); } } _domainEventsProvider.ClearAllDomainEvents(); var tasks = domainEvents .Select(async(domainEvent) => { await _mediator.Publish(domainEvent); }); await Task.WhenAll(tasks); foreach (var domainEventNotification in domainEventNotifications) { string type = domainEventNotification.GetType().FullName; var data = JsonConvert.SerializeObject(domainEventNotification, new JsonSerializerSettings { ContractResolver = new AllPropertiesContractResolver() }); OutboxMessage outboxMessage = new OutboxMessage( domainEventNotification.Id, domainEventNotification.DomainEvent.OccurredOn, type, data); _outbox.Add(outboxMessage); } }
public Task <OutboxMessage> Get(string messageId, ContextBag context) { object[] possibleIds = { EndpointQualifiedMessageId(messageId), messageId, }; if (Transaction.Current != null) { throw new Exception("The endpoint is configured to use Outbox but a TransactionScope has been detected. Outbox mode is not compatible with " + $"TransactionScope. Do not configure the transport to use '{nameof(TransportTransactionMode.TransactionScope)}' transaction mode with Outbox."); } using (new TransactionScope(TransactionScopeOption.Suppress)) { TEntity result; using (var session = sessionFactory.OpenStatelessSession()) { using (var tx = session.BeginTransaction(IsolationLevel.ReadCommitted)) { //Explicitly using ICriteria instead of QueryOver for performance reasons. //It seems QueryOver uses quite a bit reflection and that takes longer. result = session.CreateCriteria <TEntity>() .Add(Restrictions.In(nameof(IOutboxRecord.MessageId), possibleIds)) .UniqueResult <TEntity>(); tx.Commit(); } } if (result == null) { return(Task.FromResult <OutboxMessage>(null)); } if (result.Dispatched) { return(Task.FromResult(new OutboxMessage(result.MessageId, new TransportOperation[0]))); } var transportOperations = ConvertStringToObject(result.TransportOperations) .Select(t => new TransportOperation(t.MessageId, t.Options, t.Message, t.Headers)) .ToArray(); var message = new OutboxMessage(result.MessageId, transportOperations); return(Task.FromResult(message)); } }
public bool TryGet(string messageId, out OutboxMessage message) { if (SkipGetOnce) { message = null; SkipGetOnce = false; Console.Out.WriteLine("Monkey: Message {0} was skipped, heheheh", messageId); return(false); } var found = InnerPersister.TryGet(messageId, out message); Console.Out.WriteLine("Monkey: Message {0} was {1} in outbox", messageId, found ? "found" : "not found"); return(found); }
public async Task DispatchEventsAsync() { var domainEvents = _domainEventsAccessor.GetAllDomainEvents(); var domainEventNotifications = new List <IDomainEventNotification <IDomainEvent> >(); foreach (var domainEvent in domainEvents) { Type domainEvenNotificationType = typeof(IDomainEventNotification <>); var domainNotificationWithGenericType = domainEvenNotificationType.MakeGenericType(domainEvent.GetType()); var domainNotification = _scope.ResolveOptional(domainNotificationWithGenericType, new List <Parameter> { new NamedParameter("domainEvent", domainEvent), new NamedParameter("id", domainEvent.Id) }); if (domainNotification != null) { domainEventNotifications.Add(domainNotification as IDomainEventNotification <IDomainEvent>); } } _domainEventsAccessor.ClearAllDomainEvents(); foreach (var domainEvent in domainEvents) { await _mediator.Publish(domainEvent); } foreach (var domainEventNotification in domainEventNotifications) { var type = _domainNotificationsMapper.GetName(domainEventNotification.GetType()); var data = JsonConvert.SerializeObject(domainEventNotification, new JsonSerializerSettings { ContractResolver = new AllPropertiesContractResolver() }); var outboxMessage = new OutboxMessage( domainEventNotification.Id, domainEventNotification.DomainEvent.OccurredOn, type, data); _outbox.Add(outboxMessage); } }
public async Task DispatchEventsAsync() { var domainEntities = this._context.ChangeTracker .Entries <Entity>() .Where(x => x.Entity.DomainEvents != null && x.Entity.DomainEvents.Any()).ToList(); var domainEvents = domainEntities .SelectMany(x => x.Entity.DomainEvents) .ToList(); foreach (var ev in domainEvents) { Console.WriteLine(ev.OccurredOn); } var domainEventNotifications = new List <IDomainEventNotification <IDomainEvent> >(); foreach (var domainEvent in domainEvents) { Type domainEventNotificationType = typeof(IDomainEventNotification <>); var domainNotificationWithGenericType = domainEventNotificationType.MakeGenericType(domainEvent.GetType()); var domainNotification = _lifetimeScope.ResolveOptional(domainNotificationWithGenericType, new List <Parameter> { new NamedParameter("domainEvent", domainEvent) }); if (domainNotification != null) { domainEventNotifications.Add(domainNotification as IDomainEventNotification <IDomainEvent>); } } domainEntities .ForEach(entity => entity.Entity.ClearDomainEvents()); foreach (var domainEventNotification in domainEventNotifications) { string type = domainEventNotification.GetType().FullName; var data = JsonConvert.SerializeObject(domainEventNotification); OutboxMessage outboxMessage = new OutboxMessage( domainEventNotification.DomainEvent.OccurredOn, type, data); await this._context.OutboxMessages.AddAsync(outboxMessage); } }
public async Task Should_clear_operations_on_dispatched_messages() { configuration.RequiresOutboxSupport(); var storage = configuration.OutboxStorage; var ctx = configuration.GetContextBagForOutbox(); var messageId = Guid.NewGuid().ToString(); await storage.Get(messageId, ctx, default); var messageToStore = new OutboxMessage(messageId, new[] { new TransportOperation("x", null, null, null) }); using (var transaction = await storage.BeginTransaction(ctx, default)) { await storage.Store(messageToStore, transaction, ctx, default); await transaction.Commit(default);
public void New_should_create_valid_instance() { var expectedId = Guid.NewGuid(); var expectedType = "lorem"; ReadOnlyMemory <byte> expectedData = new byte[] { 1, 2, 3, 4 }; var expectedCorrelationId = Guid.NewGuid(); var sut = OutboxMessage.New(expectedId, expectedData, expectedType, expectedCorrelationId); sut.Id.Should().Be(expectedId); sut.Data.Should().BeEquivalentTo(expectedData); sut.Type.Should().Be(expectedType); sut.Status.Should().Be(OutboxMessage.MessageStatuses.Pending); sut.PartitionKey.Should().Be(expectedCorrelationId.ToString()); sut.PublishingDate.Should().BeNull(); sut.LockId.Should().BeNull(); sut.LockTime.Should().BeNull(); }
public void Should_set_defaults() { var outboxMessage = new OutboxMessage( this.Message.MessageType, this.MessageBytes, this.SerializedMessageMetaData, DateTime.UtcNow ); outboxMessage.Id.ShouldNotBe(default(Guid)); outboxMessage.TryCount.ShouldBe(0); outboxMessage.CreatedAtUtc.ShouldBeInRange(DateTime.UtcNow.AddSeconds(-10), DateTime.UtcNow); outboxMessage.ExpiresAtUtc.ShouldBeNull(); outboxMessage.Endpoint.ShouldBeNull(); outboxMessage.SkipTransientDispatch.ShouldBeFalse(); outboxMessage.Status.ShouldBe(1);//when transient dispatch enabled }
public override async Task Complete(OutboxMessage outboxMessage, DbConnection connection, DbTransaction transaction, ContextBag context, CancellationToken cancellationToken = default) { string json = Serializer.Serialize(outboxMessage.TransportOperations.ToSerializable()); json = sqlDialect.AddOutboxPadding(json); using (var command = sqlDialect.CreateCommand(connection)) { command.CommandText = outboxCommands.PessimisticComplete; command.Transaction = transaction; command.AddParameter("MessageId", outboxMessage.MessageId); command.AddJsonParameter("Operations", json); _ = await command.ExecuteNonQueryEx(cancellationToken).ConfigureAwait(false); } }
void VerifyOperationsAreEmpty(OutboxMessage result) { using (var connection = dbConnection()) { connection.Open(); using (var command = connection.CreateCommand()) { command.CommandText = BuildOperationsFromMessageIdCommand(result.MessageId); using (var reader = command.ExecuteReader()) { reader.Read(); var operations = reader.GetString(0); Assert.AreEqual("[]", operations); } } } }
public async Task Store(OutboxMessage message, OutboxTransaction transaction, ContextBag context) { var operations = new StoredTransportOperation[message.TransportOperations.Length]; for (var i = 0; i < message.TransportOperations.Length; i++) { var t = message.TransportOperations[i]; operations[i] = new StoredTransportOperation(t.MessageId, t.Options, t.Body, t.Headers); } var tx = ((ServiceFabricOutboxTransaction)transaction).Transaction.NativeTransaction; if (!await Outbox.TryAddAsync(tx, message.MessageId, new StoredOutboxMessage(message.MessageId, operations)).ConfigureAwait(false)) { throw new Exception($"Outbox message with id '{message.MessageId}' is already present in storage."); } }
public static Task Execute( IDbConnection dbConnection, IConfiguration configuration, OutboxMessage outboxMessage) { var objectSerializer = new ObjectSerializer(); var @params = new DynamicParameters(); @params.Add(CommonColumns.Id, outboxMessage.Id); @params.Add(Columns.OutgoingMessageHeaders, objectSerializer.Serialize(outboxMessage.Headers)); @params.Add(Columns.OutgoingMessageObject, objectSerializer.Serialize(outboxMessage.Message)); @params.Add(Columns.OutgoingSendFunction, outboxMessage.SendFunction); var sql = string.Format(SqlTemplate, configuration.GetDbSchema(), Tables.OutgoingMessagesTable); return(dbConnection.ExecuteAsync(sql, @params)); }
public void Should_set_required_properties() { var priorityDate = DateTime.UtcNow; var outboxMessage = new OutboxMessage( this.Message.MessageType, this.MessageBytes, this.SerializedMessageMetaData, priorityDate ); outboxMessage.Id.ShouldNotBe(default(Guid)); outboxMessage.Type.ShouldBe(this.Message.MessageType); outboxMessage.MessageData.ShouldNotBeNull(); outboxMessage.MessageData.Data.ShouldBe(this.MessageBytes); outboxMessage.MessageData.MetaData.ShouldBe(this.SerializedMessageMetaData); outboxMessage.PriorityDateUtc.ShouldBe(priorityDate); }
public async Task <Bookmark> AddBookmarkAsync(AddBookmarkRequest request) { var bookmark = new Bookmark(request.Url); _context.Bookmarks.Add(bookmark); var outboxMessage = new OutboxMessage( "bookmark.inserted", new BookmarkInserted(bookmark.Id, bookmark.Url)); _context.OutboxMessages.Add(outboxMessage); await _context.SaveChangesAsync(); _logger.LogInformation("Bookmark {url} added", request.Url); return(bookmark); }
public void Should_calculate_expired_when_expiration_set() { var expiresAt = new DateTime(2019, 6, 3, 8, 0, 0, DateTimeKind.Utc); var outboxMessage = new OutboxMessage( this.Message.MessageType, this.MessageBytes, this.SerializedMessageMetaData, DateTime.UtcNow, false, expiresAt ); outboxMessage.ExpiresAtUtc.ShouldBe(expiresAt); outboxMessage.IsExpired(expiresAt.AddMilliseconds(-1)).ShouldBeFalse(); outboxMessage.IsExpired(expiresAt).ShouldBeTrue(); outboxMessage.IsExpired(expiresAt.AddMilliseconds(1)).ShouldBeTrue(); }
public async Task Should_delete_all_OutboxRecords_that_have_been_dispatched() { var id = Guid.NewGuid().ToString("N"); var context = new ContextBag(); var persister = new OutboxPersister(store, "TestEndpoint"); using (var transaction = await persister.BeginTransaction(context)) { await persister.Store(new OutboxMessage("NotDispatched", new TransportOperation[0]), transaction, context); await transaction.Commit(); } var outboxMessage = new OutboxMessage(id, new[] { new TransportOperation(id, new Dictionary <string, string>(), new byte[1024 * 5], new Dictionary <string, string>()) }); using (var transaction = await persister.BeginTransaction(context)) { await persister.Store(outboxMessage, transaction, context); await transaction.Commit(); } await persister.SetAsDispatched(id, context); await Task.Delay(TimeSpan.FromSeconds(1)); //Need to wait for dispatch logic to finish var cleaner = new OutboxRecordsCleaner(store); await cleaner.RemoveEntriesOlderThan(DateTime.UtcNow.AddMinutes(1)); using (var s = store.OpenSession()) { var result = s.Query <OutboxRecord>().ToList(); Assert.AreEqual(1, result.Count); Assert.AreEqual("NotDispatched", result[0].MessageId); } }
public Task Store(OutboxMessage message, IOutboxTransaction transaction, ContextBag context, CancellationToken cancellationToken = default) { var cosmosTransaction = (CosmosOutboxTransaction)transaction; if (cosmosTransaction == null || cosmosTransaction.SuppressStoreAndCommit || cosmosTransaction.PartitionKey == null) { return(Task.CompletedTask); } cosmosTransaction.StorageSession.AddOperation(new OutboxStore(new OutboxRecord { Id = message.MessageId, TransportOperations = message.TransportOperations.Select(op => new StorageTransportOperation(op)).ToArray() }, cosmosTransaction.PartitionKey.Value, serializer, context)); return(Task.CompletedTask); }
public Task Store(OutboxMessage outboxMessage, OutboxTransaction transaction, ContextBag context) { var operations = outboxMessage.TransportOperations.Select(t => new OutboxOperation { Message = t.Body, Headers = t.Headers, MessageId = t.MessageId, Options = t.Options, }); var nhibernateTransaction = (NHibernateOutboxTransaction)transaction; nhibernateTransaction.Session.Save(new OutboxRecord { MessageId = EndpointQualifiedMessageId(outboxMessage.MessageId), Dispatched = false, TransportOperations = ConvertObjectToString(operations) }); return(Task.FromResult(0)); }
public async Task <int> CommitAsync(CancellationToken cancellationToken = default(CancellationToken)) { var notifications = await this._domainEventsDispatcher.DispatchEventsAsync(this); foreach (var domainEventNotification in notifications) { string type = domainEventNotification.GetType().FullName; var data = JsonConvert.SerializeObject(domainEventNotification); OutboxMessage outboxMessage = new OutboxMessage( domainEventNotification.DomainEvent.OccurredOn, type, data); this.OutboxMessages.Add(outboxMessage); } var saveResult = await base.SaveChangesAsync(cancellationToken); return(saveResult); }
public bool TryGet(string messageId, out OutboxMessage message) { object[] possibleIds = { EndpointQualifiedMessageId(messageId), messageId, }; using (new TransactionScope(TransactionScopeOption.Suppress)) { try { OutboxEntity result; using (var tx = _dbContext.Database.BeginTransaction(System.Data.IsolationLevel.ReadCommitted)) { //todo: make this faster result = _dbContext.OutboxRecords.ToArray().SingleOrDefault(e => possibleIds.Contains(e.MessageId)); tx.Commit(); } if (result == null) { message = null; return(false); } if (result.Dispatched) { message = new OutboxMessage(result.MessageId); return(true); } var transportOperations = ConvertStringToObject(result.TransportOperations) .Select(t => new TransportOperation(t.MessageId, t.Options, t.Message, t.Headers)) .ToList(); message = new OutboxMessage(result.MessageId); message.TransportOperations.AddRange(transportOperations); return(true); } catch (Exception e) { throw; } } }
public async Task <OutboxMessage> Get(string messageId, ContextBag context, CancellationToken cancellationToken = default) { using (new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)) using (var connection = await connectionManager.OpenConnection(context.GetIncomingMessage(), cancellationToken).ConfigureAwait(false)) using (var transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted)) { OutboxMessage result; using (var command = sqlDialect.CreateCommand(connection)) { command.CommandText = outboxCommands.Get; command.Transaction = transaction; command.AddParameter("MessageId", messageId); // to avoid loading into memory SequentialAccess is required which means each fields needs to be accessed, but SequentialAccess is unsupported for SQL Server AlwaysEncrypted using (var dataReader = await command.ExecuteReaderAsync(CommandBehavior.SequentialAccess | CommandBehavior.SingleRow, cancellationToken).ConfigureAwait(false)) { if (!await dataReader.ReadAsync(cancellationToken).ConfigureAwait(false)) { return(null); } var dispatched = await dataReader.GetBoolAsync(0, cancellationToken).ConfigureAwait(false); using (var textReader = dataReader.GetTextReader(1)) { if (dispatched) { result = new OutboxMessage(messageId, new TransportOperation[0]); } else { var transportOperations = Serializer.Deserialize <IEnumerable <SerializableOperation> >(textReader) .FromSerializable() .ToArray(); result = new OutboxMessage(messageId, transportOperations); } } } } transaction.Commit(); return(result); } }
public async Task Should_store_when_transaction_committed() { var storage = new NonDurableOutboxStorage(); var messageId = "myId"; var contextBag = new ContextBag(); using (var transaction = await storage.BeginTransaction(contextBag)) { var messageToStore = new OutboxMessage(messageId, new[] { new TransportOperation("x", null, null, null) }); await storage.Store(messageToStore, transaction, contextBag); await transaction.Commit(); } var message = await storage.Get(messageId, new ContextBag()); Assert.NotNull(message); }
public async Task Should_not_remove_non_dispatched_messages() { var storage = new InMemoryOutboxStorage(); var messageId = "myId"; var messageToStore = new OutboxMessage(messageId, new[] { new TransportOperation("x", null, null, null) }); using (var transaction = await storage.BeginTransaction(new ContextBag())) { await storage.Store(messageToStore, transaction, new ContextBag()); await transaction.Commit(); } storage.RemoveEntriesOlderThan(DateTime.UtcNow); var message = await storage.Get(messageId, new ContextBag()); Assert.NotNull(message); }
public static async Task <OutboxMessage> CreateOutboxMessage(string endpointName, string uniqueId, OutboxMessageStatus status, DateTime?priorityDateUtc = null, DateTime?expiresAtUtc = null) { var messageMetaData = new MessageMetaData(messageId: uniqueId); var serializedMessageMetaData = messageMetaData != null?JsonConvert.SerializeObject(messageMetaData) : null; OutboxMessage om = new OutboxMessage( Message.MessageType, MessageBytes, serializedMessageMetaData, priorityDateUtc.HasValue ? priorityDateUtc.Value : DateTime.UtcNow, endpointName, true, expiresAtUtc ) { Status = (int)status, }; return(await CreateOutboxMessage(om)); }
public async Task Should_clear_operations_on_dispatched_messages() { var storage = new InMemoryOutboxStorage(); var messageId = "myId"; var messageToStore = new OutboxMessage(messageId, new[] { new TransportOperation("x", null, null, null) }); using (var transaction = await storage.BeginTransaction(new ContextBag())) { await storage.Store(messageToStore, transaction, new ContextBag()); await transaction.Commit(); } await storage.SetAsDispatched(messageId, new ContextBag()); var message = await storage.Get(messageId, new ContextBag()); Assert.False(message.TransportOperations.Any()); }
public Task StoreAsync(OutboxMessage outboxMessage, IOutboxTransaction outboxTransaction) { var msSqlOutboxTransaction = (OutboxTransaction)outboxTransaction; var transaction = msSqlOutboxTransaction.Transaction; using (var command = _connection.CreateCommand()) { command.CommandText = StoreCommandText; command.CommandType = CommandType.Text; command.Transaction = transaction; command.AddParameter("MessageId", outboxMessage.MessageId); command.AddParameter("EndpointName", outboxMessage.EndpointName); command.AddParameter("CreatedAt", DateTime.UtcNow); command.AddParameter("Operations", JsonConvert.SerializeObject(outboxMessage.Operations, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto })); return(command.ExecuteNonQueryAsync()); } }
public async Task Should_not_store_when_transaction_not_commited() { var storage = new InMemoryOutboxStorage(); var messageId = "myId"; var contextBag = new ContextBag(); using (var transaction = await storage.BeginTransaction(contextBag)) { var messageToStore = new OutboxMessage(messageId, new[] { new TransportOperation("x", null, null, null) }); await storage.Store(messageToStore, transaction, contextBag); // do not commit } var message = await storage.Get(messageId, new ContextBag()); Assert.Null(message); }
public async Task Should_be_deleted() { await store.Maintenance.SendAsync( new ConfigureExpirationOperation( new ExpirationConfiguration { Disabled = false, DeleteFrequencyInSec = 1, })); // arrange var persister = new OutboxPersister("TestEndpoint", CreateTestSessionOpener(), TimeSpan.FromSeconds(1)); var context = new ContextBag(); var incomingMessageId = SimulateIncomingMessage(context).MessageId; var dispatchedOutboxMessage = new OutboxMessage(incomingMessageId, new TransportOperation[0]); var notDispatchedOutboxMessage = new OutboxMessage("NotDispatched", new TransportOperation[0]); using (var transaction = await persister.BeginTransaction(context)) { await persister.Store(dispatchedOutboxMessage, transaction, context); await persister.Store(notDispatchedOutboxMessage, transaction, context); await transaction.Commit(); } await persister.SetAsDispatched(dispatchedOutboxMessage.MessageId, context); // act // wait for dispatch logic and expiry to finish, not ideal but polling on BASE index is also not great await Task.Delay(TimeSpan.FromSeconds(3)); WaitForIndexing(); // assert using (var session = store.OpenAsyncSession()) { var outboxRecords = await session.Query <OutboxRecord>().ToListAsync(); Assert.AreEqual(1, outboxRecords.Count); Assert.AreEqual(notDispatchedOutboxMessage.MessageId, outboxRecords.Single().MessageId); } }
public Task <OutboxMessage> Get(string messageId, ContextBag context) { object[] possibleIds = { EndpointQualifiedMessageId(messageId), messageId, }; using (new TransactionScope(TransactionScopeOption.Suppress)) { OutboxRecord result; using (var session = sessionFactory.OpenStatelessSession()) { using (var tx = session.BeginTransaction(IsolationLevel.ReadCommitted)) { //Explicitly using ICriteria instead of QueryOver for performance reasons. //It seems QueryOver uses quite a bit reflection and that takes longer. result = session.CreateCriteria <OutboxRecord>() .Add(Restrictions.In(nameof(OutboxRecord.MessageId), possibleIds)) .UniqueResult <OutboxRecord>(); tx.Commit(); } } if (result == null) { return(Task.FromResult <OutboxMessage>(null)); } if (result.Dispatched) { return(Task.FromResult(new OutboxMessage(result.MessageId, new TransportOperation[0]))); } var transportOperations = ConvertStringToObject(result.TransportOperations) .Select(t => new TransportOperation(t.MessageId, t.Options, t.Message, t.Headers)) .ToArray(); var message = new OutboxMessage(result.MessageId, transportOperations); return(Task.FromResult(message)); } }
public async Task Invoke(ITransportReceiveContext context, Func <IIncomingPhysicalMessageContext, Task> next) { var messageId = context.Message.MessageId; var physicalMessageContext = this.CreateIncomingPhysicalMessageContext(context.Message, context); var deduplicationEntry = await outboxStorage.Get(messageId, context.Extensions).ConfigureAwait(false); var pendingTransportOperations = new PendingTransportOperations(); if (deduplicationEntry == null) { physicalMessageContext.Extensions.Set(pendingTransportOperations); using (var outboxTransaction = await outboxStorage.BeginTransaction(context.Extensions).ConfigureAwait(false)) { context.Extensions.Set(outboxTransaction); await next(physicalMessageContext).ConfigureAwait(false); var outboxMessage = new OutboxMessage(messageId, ConvertToOutboxOperations(pendingTransportOperations.Operations)); await outboxStorage.Store(outboxMessage, outboxTransaction, context.Extensions).ConfigureAwait(false); context.Extensions.Remove <OutboxTransaction>(); await outboxTransaction.Commit().ConfigureAwait(false); } physicalMessageContext.Extensions.Remove <PendingTransportOperations>(); } else { ConvertToPendingOperations(deduplicationEntry, pendingTransportOperations); } if (pendingTransportOperations.HasOperations) { var batchDispatchContext = this.CreateBatchDispatchContext(pendingTransportOperations.Operations, physicalMessageContext); await this.Fork(batchDispatchContext).ConfigureAwait(false); } await outboxStorage.SetAsDispatched(messageId, context.Extensions).ConfigureAwait(false); }
public async Task Should_clear_operations_on_dispatched_messages() { var storage = new InMemoryOutboxStorage(); var messageId = "myId"; var messageToStore = new OutboxMessage(messageId, new[] { new TransportOperation("x", null, null, null) }); using (var transaction = await storage.BeginTransaction(new ContextBag())) { await storage.Store(messageToStore, transaction, new ContextBag()); await transaction.Commit(); } await storage.SetAsDispatched(messageId, new ContextBag()); var message = await storage.Get(messageId, new ContextBag()); Assert.False(message.TransportOperations.Any()); }
public async Task Should_not_remove_non_dispatched_messages() { var storage = new InMemoryOutboxStorage(); var messageId = "myId"; var messageToStore = new OutboxMessage(messageId, new[] { new TransportOperation("x", null, null, null) }); using (var transaction = await storage.BeginTransaction(new ContextBag())) { await storage.Store(messageToStore, transaction, new ContextBag()); await transaction.Commit(); } storage.RemoveEntriesOlderThan(DateTime.UtcNow); var message = await storage.Get(messageId, new ContextBag()); Assert.NotNull(message); }
public void Store(string messageId, IEnumerable<TransportOperation> transportOperations) { StoredMessage = new OutboxMessage(messageId); StoredMessage.TransportOperations.AddRange(transportOperations); }
public async Task Should_not_store_when_transaction_not_commited() { var storage = new InMemoryOutboxStorage(); var messageId = "myId"; var contextBag = new ContextBag(); using (var transaction = await storage.BeginTransaction(contextBag)) { var messageToStore = new OutboxMessage(messageId, new[] { new TransportOperation("x", null, null, null) }); await storage.Store(messageToStore, transaction, contextBag); // do not commit } var message = await storage.Get(messageId, new ContextBag()); Assert.Null(message); }