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));
            }
        }
Beispiel #6
0
        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);
            }
        }
Beispiel #9
0
        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);
Beispiel #10
0
        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();
        }
Beispiel #11
0
        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
        }
Beispiel #12
0
    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);
        }
    }
Beispiel #13
0
 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);
             }
         }
     }
 }
Beispiel #14
0
        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));
        }
Beispiel #16
0
        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);
        }
Beispiel #18
0
        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);
            }
        }
Beispiel #20
0
        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);
        }
Beispiel #21
0
        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));
        }
Beispiel #22
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);
        }
Beispiel #27
0
        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());
        }
Beispiel #29
0
        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);
            }
        }
Beispiel #32
0
        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);
        }
Beispiel #36
0
 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);
        }