public async Task AbleToMigrate_fails_if_Timeout_table_Does_not_exist()
        {
            // Arrange
            var timeoutTarget = new ASQTarget(connectionString, new DelayedDeliveryTableNameProvider("TimeoutTableThatDoesNotExist"));

            // Act
            var ableToMigrate = await timeoutTarget.AbleToMigrate(new EndpointInfo { EndpointName = endpointName });

            // Assert
            Assert.IsFalse(ableToMigrate.CanMigrate);
            Assert.AreEqual("Target delayed delivery table TimeoutTableThatDoesNotExist does not exist.", ableToMigrate.Problems[0]);
        }
        public async Task Prepare_creates_the_staging_queue()
        {
            // Arrange
            var nameProvider  = new DelayedDeliveryTableNameProvider();
            var timeoutTarget = new ASQTarget(connectionString, nameProvider);

            // Act
            await using var migrator = await timeoutTarget.PrepareTargetEndpointBatchMigrator(endpointName);

            // Assert
            Assert.IsTrue(await DoesTableExist(nameProvider.GetStagingTableName(endpointName)).ConfigureAwait(true));
        }
        public async Task AbleToMigrate_passes_if_Timeout_table_exists()
        {
            // Arrange
            await CreateTimeoutTable($"T{endpointName}");

            var timeoutTarget = new ASQTarget(connectionString, new DelayedDeliveryTableNameProvider($"T{endpointName}"));

            // Act
            var ableToMigrate = await timeoutTarget.AbleToMigrate(new EndpointInfo { EndpointName = endpointName });

            // Assert
            Assert.IsTrue(ableToMigrate.CanMigrate);
        }
        public async Task Completing_with_large_entities_batches_respecting_size_limitations()
        {
            // Arrange
            var nameProvider = new DelayedDeliveryTableNameProvider();
            var endpointName = nameof(Completing_with_large_entities_batches_respecting_size_limitations);
            var cutOffDate   = DateTime.UtcNow;
            var random       = new Random();
            var timeouts     = new List <StagedDelayedMessageEntity>();

            var target           = new ASQTarget(connectionString, nameProvider);
            var stagingTableName = nameProvider.GetStagingTableName(endpointName);

            await CreateTimeoutTable(stagingTableName);
            await CreateTimeoutTable(nameProvider.GetDelayedDeliveryTableName(endpointName));

            var stagingTable = tableClient.GetTableReference(stagingTableName);

            // the entity will roughly be 98 KB and we will store 50 of those which makes the actual payload be around 5 MB
            for (var x = 0; x < 50; x++)
            {
                var dateTime  = cutOffDate.AddDays(random.Next(1, 5));
                var messageId = Guid.NewGuid().ToString();
                var entity    = new StagedDelayedMessageEntity
                {
                    Destination  = new string('a', 32 * 1024),
                    Headers      = new string('a', 32 * 1024),
                    Time         = dateTime,
                    MessageId    = messageId,
                    Body         = new byte[32 * 1024],
                    RowKey       = $"{messageId}_{dateTime.ToString($"yyyyMMddHHmmss")}",
                    PartitionKey = "1"
                };

                await stagingTable.ExecuteAsync(TableOperation.Insert(entity));

                timeouts.Add(entity);
            }

            // Act
            var result = await target.PrepareTargetEndpointBatchMigrator(endpointName);

            var completeResult = await result.CompleteBatch(1);

            // Assert
            Assert.IsNotNull(completeResult);
            Assert.AreEqual(timeouts.Count, completeResult);
        }
        public async Task Complete_Removes_Staging_Queue_If_Empty()
        {
            // Arrange
            var nameProvider = new DelayedDeliveryTableNameProvider();

            await CreateTimeoutTable(nameProvider.GetDelayedDeliveryTableName(endpointName));

            var timeoutTarget = new ASQTarget(connectionString, nameProvider);

            await using var migrator = await timeoutTarget.PrepareTargetEndpointBatchMigrator(endpointName);

            await migrator.StageBatch(new List <TimeoutData>
            {
                new TimeoutData
                {
                    Id      = "SomeID",
                    Headers = new Dictionary <string, string>
                    {
                        { "NServiceBus.MessageId", "SomeMessageId" }
                    },
                    Destination = "SomeDestination",
                    State       = new byte[2],
                    Time        = new DateTime(2021, 12, 12, 12, 12, 12, DateTimeKind.Utc)
                },
                new TimeoutData
                {
                    Id      = "SomeOtherId",
                    Headers = new Dictionary <string, string>
                    {
                        { "NServiceBus.MessageId", "SomeOtherMessageId" }
                    },
                    Destination = "SomeOtherDestination",
                    State       = new byte[2],
                    Time        = new DateTime(2021, 12, 12, 12, 13, 13, DateTimeKind.Utc)
                },
            }, 1);

            await migrator.CompleteBatch(1);

            // Act
            await timeoutTarget.Complete(endpointName);

            // Assert
            var stagingTableExists = await DoesTableExist(nameProvider.GetStagingTableName(endpointName));

            Assert.IsFalse(stagingTableExists);
        }
        public async Task CompleteBatch_Moves_All_Entries_From_Staging_To_DelayedMessageTable()
        {
            // Arrange
            var nameProvider = new DelayedDeliveryTableNameProvider();

            await CreateTimeoutTable(nameProvider.GetDelayedDeliveryTableName(endpointName));

            var timeoutTarget = new ASQTarget(connectionString, nameProvider);

            await using var migrator = await timeoutTarget.PrepareTargetEndpointBatchMigrator(endpointName);

            await migrator.StageBatch(new List <TimeoutData>
            {
                new TimeoutData
                {
                    Id      = "SomeID",
                    Headers = new Dictionary <string, string>
                    {
                        { "NServiceBus.MessageId", "SomeMessageId" }
                    },
                    Destination = "SomeDestination",
                    State       = new byte[2],
                    Time        = new DateTime(2021, 12, 12, 12, 12, 12, DateTimeKind.Utc)
                },
                new TimeoutData
                {
                    Id      = "SomeOtherId",
                    Headers = new Dictionary <string, string>
                    {
                        { "NServiceBus.MessageId", "SomeOtherMessageId" }
                    },
                    Destination = "SomeOtherDestination",
                    State       = new byte[2],
                    Time        = new DateTime(2021, 12, 12, 12, 13, 13, DateTimeKind.Utc)
                },
            }, 1);

            // Act
            var numberCompleted = await migrator.CompleteBatch(1);

            // Assert
            var recordsInTimeoutTable = await ReadTimeoutsFromTable(nameProvider.GetDelayedDeliveryTableName(endpointName));

            Assert.AreEqual(2, recordsInTimeoutTable.Count);
            Assert.AreEqual(2, numberCompleted);
        }
        public async Task AbleToMigrate_fails_with_incorrect_connection_string()
        {
            // Arrange
            string fakeConnectionString = "DefaultEndpointsProtocol=https;AccountName=fakename;AccountKey=g94OvNO9o3sVan5eipemQEHmU8zD2M9iq98E8nKSdR2bTuQB1hi07Yd1/8dDw6+1jGI2klWjpvoDahHPhR/3og==";
            var    account = CloudStorageAccount.Parse(fakeConnectionString);
            var    client  = account.CreateCloudTableClient();

            client.DefaultRequestOptions.MaximumExecutionTime = TimeSpan.FromSeconds(2);

            var timeoutTarget = new ASQTarget(client, new DelayedDeliveryTableNameProvider("TimeoutTableThatDoesNotExist"));

            // Act
            var ableToMigrate = await timeoutTarget.AbleToMigrate(new EndpointInfo { EndpointName = endpointName });

            // Assert
            Assert.IsFalse(ableToMigrate.CanMigrate);
            Assert.That(ableToMigrate.Problems[0], Does.StartWith("Unable to connect to the storage instance on account 'fakename'. Verify the connection string. Exception message '"));
        }
        public async Task Complete_Throws_If_Messages_Are_Still_Staged()
        {
            // Arrange
            var nameProvider = new DelayedDeliveryTableNameProvider();

            await CreateTimeoutTable(nameProvider.GetDelayedDeliveryTableName(endpointName));

            var timeoutTarget = new ASQTarget(connectionString, nameProvider);

            await using var migrator = await timeoutTarget.PrepareTargetEndpointBatchMigrator(endpointName);

            var numberStaged = await migrator.StageBatch(new List <TimeoutData>
            {
                new TimeoutData
                {
                    Id      = "SomeID",
                    Headers = new Dictionary <string, string>
                    {
                        { "NServiceBus.MessageId", "SomeMessageId" }
                    },
                    Destination = "SomeDestination",
                    State       = new byte[2],
                    Time        = new DateTime(2021, 12, 12, 12, 12, 12, DateTimeKind.Utc)
                },
                new TimeoutData
                {
                    Id      = "SomeOtherId",
                    Headers = new Dictionary <string, string>
                    {
                        { "NServiceBus.MessageId", "SomeOtherMessageId" }
                    },
                    Destination = "SomeOtherDestination",
                    State       = new byte[2],
                    Time        = new DateTime(2021, 12, 12, 12, 13, 13, DateTimeKind.Utc)
                },
            }, 1);

            // Assert
            Assert.ThrowsAsync <Exception>(async() =>
            {
                // Act
                await timeoutTarget.Complete(endpointName);
            });
        }
        public async Task Abort_Removes_Staging_Table()
        {
            // Arrange
            var nameProvider = new DelayedDeliveryTableNameProvider();

            await CreateTimeoutTable(nameProvider.GetDelayedDeliveryTableName(endpointName));

            var timeoutTarget = new ASQTarget(connectionString, nameProvider);

            await using var migrator = await timeoutTarget.PrepareTargetEndpointBatchMigrator(endpointName);

            // Act
            await timeoutTarget.Abort(endpointName);

            // Assert
            var stagingTableExists = await DoesTableExist(nameProvider.GetStagingTableName(endpointName));

            Assert.IsFalse(stagingTableExists);
        }
        public async Task Staging_with_large_entities_batches_respecting_size_limitations()
        {
            // Arrange
            var nameProvider = new DelayedDeliveryTableNameProvider();
            var endpointName = nameof(Staging_with_large_entities_batches_respecting_size_limitations);
            var cutOffDate   = DateTime.UtcNow;
            var random       = new Random();
            var target       = new ASQTarget(connectionString, nameProvider);
            var timeouts     = new List <TimeoutData>();

            // the entity will roughly be 98 KB and we will store 50 of those which makes the actual payload be around 5 MB
            for (var x = 0; x < 50; x++)
            {
                var dateTime = cutOffDate.AddDays(random.Next(1, 5));

                timeouts.Add(new TimeoutData
                {
                    Destination = new string('a', 32 * 1024),
                    Headers     = new Dictionary <string, string> {
                        { "a", new string('h', 16 * 1024) }
                    },
                    Time   = dateTime,
                    Id     = Guid.NewGuid().ToString(),
                    State  = new byte[32 * 1024],
                    SagaId = Guid.NewGuid(),
                    OwningTimeoutManager = this.endpointName
                });
            }

            // Act
            var result = await target.PrepareTargetEndpointBatchMigrator(endpointName);

            var stageResult = await result.StageBatch(timeouts, 1);

            // Assert
            Assert.IsNotNull(stageResult);
            Assert.AreEqual(timeouts.Count, stageResult);
        }
        public async Task Can_migrate_timeouts()
        {
            var sourceEndpoint = NServiceBus.AcceptanceTesting.Customization.Conventions.EndpointNamingConvention(typeof(RavenDBSource));
            var targetEndpoint = NServiceBus.AcceptanceTesting.Customization.Conventions.EndpointNamingConvention(typeof(AsqTarget));

            var ravenTimeoutPrefix = "TimeoutDatas";
            var ravenVersion       = RavenDbVersion.Four;

            var ravenAdapter = new Raven4Adapter(serverUrl, databaseName);

            await Scenario.Define <SourceContext>()
            .WithEndpoint <RavenDBSource>(b => b.CustomConfig(ec =>
            {
                ec.UsePersistence <RavenDBPersistence>()
                .SetDefaultDocumentStore(GetDocumentStore(serverUrl, databaseName));
                ec.UseSerialization <NewtonsoftSerializer>();
            })
                                          .When(async(session, c) =>
            {
                var delayedMessage = new DelayedMessage();

                var options = new SendOptions();

                options.DelayDeliveryWith(TimeSpan.FromSeconds(20));
                options.SetDestination(targetEndpoint);

                await session.Send(delayedMessage, options);

                await WaitUntilTheTimeoutIsSavedInRaven(ravenAdapter, sourceEndpoint);

                c.TimeoutSet = true;
            }))
            .Done(c => c.TimeoutSet)
            .Run(TimeSpan.FromSeconds(15));

            var context = await Scenario.Define <TargetContext>()
                          // Create the legacy endpoint to forward the delayed message to the native delayed delivery endpoint
                          // This is needed as ASQ stores the delayed messages at the sending endpoint until delivery is needed
                          .WithEndpoint <RavenDBSource>(b => b.CustomConfig(ec =>
            {
                var transport = ec.UseTransport <AzureStorageQueueTransport>().ConnectionString(asqConnectionString);
                transport.DisablePublishing();

                transport.DelayedDelivery().DisableTimeoutManager();

                ec.UseSerialization <NewtonsoftSerializer>();
            }))
                          .WithEndpoint <AsqTarget>(b => b.CustomConfig(ec =>
            {
                var transport = ec.UseTransport <AzureStorageQueueTransport>().ConnectionString(asqConnectionString);
                transport.DisablePublishing();

                transport.DelayedDelivery().DisableTimeoutManager();

                ec.UseSerialization <NewtonsoftSerializer>();
            })
                                                    .When(async(_, c) =>
            {
                var logger          = new TestLoggingAdapter(c);
                var timeoutsSource  = new RavenDbTimeoutsSource(logger, serverUrl, databaseName, ravenTimeoutPrefix, ravenVersion, false);
                var timeoutsTarget  = new ASQTarget(asqConnectionString, new DelayedDeliveryTableNameProvider());
                var migrationRunner = new MigrationRunner(logger, timeoutsSource, timeoutsTarget);

                await migrationRunner.Run(DateTime.Now.AddDays(-1), EndpointFilter.SpecificEndpoint(sourceEndpoint), new Dictionary <string, string>());
            }))
                          .Done(c => c.GotTheDelayedMessage)
                          .Run(TimeSpan.FromSeconds(30));

            Assert.True(context.GotTheDelayedMessage);
        }
        public async Task Can_migrate_timeouts()
        {
            var sourceEndpoint = NServiceBus.AcceptanceTesting.Customization.Conventions.EndpointNamingConvention(typeof(AspSource));
            var targetEndpoint = NServiceBus.AcceptanceTesting.Customization.Conventions.EndpointNamingConvention(typeof(AsqTarget));

            await Scenario.Define <SourceContext>()
            .WithEndpoint <AspSource>(b => b.CustomConfig(ec =>
            {
                SetupPersistence(ec);

                ec.UseSerialization <NewtonsoftSerializer>();
            })
                                      .When(async(session, c) =>
            {
                var delayedMessage = new DelayedMessage();

                var options = new SendOptions();

                options.DelayDeliveryWith(TimeSpan.FromSeconds(15));
                options.SetDestination(targetEndpoint);

                await session.Send(delayedMessage, options);

                await WaitUntilTheTimeoutsAreSavedInAsp(sourceEndpoint, 2);

                c.TimeoutSet = true;
            }))
            .Done(c => c.TimeoutSet)
            .Run(TimeSpan.FromSeconds(30));

            var context = await Scenario.Define <TargetContext>()
                          // Create the legacy endpoint to forward the delayed message to the reporting endpoint
                          // This is needed as ASQ stores the delayed messages at the sending endpoint until
                          // delivery is needed
                          .WithEndpoint <AspSource>(b => b.CustomConfig(ec =>
            {
                var transportConfig = ec.UseTransport <AzureStorageQueueTransport>();
                transportConfig.ConnectionString(asqConnectionString);
                transportConfig.DisablePublishing();

                transportConfig.DelayedDelivery().DisableTimeoutManager();

                ec.UseSerialization <NewtonsoftSerializer>();
            }))
                          // Start the reporting endpoint to receive and process the delayed message
                          .WithEndpoint <AsqTarget>(b => b.CustomConfig(ec =>
            {
                var transportConfig = ec.UseTransport <AzureStorageQueueTransport>();
                transportConfig.ConnectionString(asqConnectionString);
                transportConfig.DisablePublishing();

                transportConfig.DelayedDelivery().DisableTimeoutManager();

                ec.UseSerialization <NewtonsoftSerializer>();
            })
                                                    .When(async(_, c) =>
            {
                var logger          = new TestLoggingAdapter(c);
                var timeoutStorage  = CreateTimeoutStorage(sourceEndpoint);
                var timeoutsTarget  = new ASQTarget(asqConnectionString, new DelayedDeliveryTableNameProvider());
                var migrationRunner = new MigrationRunner(logger, timeoutStorage, timeoutsTarget);

                await migrationRunner.Run(DateTime.Now.AddDays(-10), EndpointFilter.SpecificEndpoint(sourceEndpoint), new Dictionary <string, string>());
            }))
                          .Done(c => c.GotTheDelayedMessage)
                          .Run(TimeSpan.FromSeconds(30));

            Assert.True(context.GotTheDelayedMessage);
        }
Exemple #13
0
        public async Task Can_migrate_timeouts()
        {
            var salesEndpoint     = NServiceBus.AcceptanceTesting.Customization.Conventions.EndpointNamingConvention(typeof(SqlPSource));
            var reportingEndpoint = NServiceBus.AcceptanceTesting.Customization.Conventions.EndpointNamingConvention(typeof(AsqTarget));

            // Make sure delayed delivery queue is created so that the migration can run.
            await Scenario.Define <SourceContext>()
            .WithEndpoint <SqlPSource>(b => b.CustomConfig(ec =>
            {
                var transportConfig = ec.UseTransport <AzureStorageQueueTransport>();
                transportConfig.ConnectionString(asqConnectionString);
                transportConfig.DisablePublishing();

                transportConfig.DelayedDelivery().DisableTimeoutManager();

                ec.UseSerialization <NewtonsoftSerializer>();
            })).Run(TimeSpan.FromSeconds(10));

            // Sending a delayed delivery message using TimeoutManager
            await Scenario.Define <SourceContext>()
            .WithEndpoint <SqlPSource>(b => b.CustomConfig(ec =>
            {
                var persistence = ec.UsePersistence <SqlPersistence>();
                persistence.SubscriptionSettings().DisableCache();

                persistence.SqlDialect <NServiceBus.SqlDialect.MsSqlServer>();
                persistence.ConnectionBuilder(
                    connectionBuilder: () => new SqlConnection(connectionString));

                ec.UseSerialization <NewtonsoftSerializer>();
            })
                                       .When(async(session, c) =>
            {
                var delayedMessage = new DelayedMessage();

                var options = new SendOptions();

                options.DelayDeliveryWith(TimeSpan.FromSeconds(15));
                options.SetDestination(reportingEndpoint);

                await session.Send(delayedMessage, options);

                await WaitUntilTheTimeoutIsSavedInSql(salesEndpoint);

                c.TimeoutSet = true;
            }))
            .Done(c => c.TimeoutSet)
            .Run(TimeSpan.FromSeconds(30));

            var context = await Scenario.Define <TargetContext>()
                          // Create the sales endpoint to forward the delayed message to the reporting endpoint
                          // This is needed as ASQ stores the delayed messages at the sending endpoint until
                          // delivery is needed
                          .WithEndpoint <SqlPSource>(b => b.CustomConfig(ec =>
            {
                var transportConfig = ec.UseTransport <AzureStorageQueueTransport>();
                transportConfig.ConnectionString(asqConnectionString);
                transportConfig.DisablePublishing();

                transportConfig.DelayedDelivery().DisableTimeoutManager();

                ec.UseSerialization <NewtonsoftSerializer>();
            }))
                          // Start the reporting endpoint to receieve and process the delayed message
                          .WithEndpoint <AsqTarget>(b => b.CustomConfig(ec =>
            {
                var transportConfig = ec.UseTransport <AzureStorageQueueTransport>();
                transportConfig.ConnectionString(asqConnectionString);
                transportConfig.DisablePublishing();

                transportConfig.DelayedDelivery().DisableTimeoutManager();

                ec.UseSerialization <NewtonsoftSerializer>();
            })
                                                    .When(async(_, c) =>
            {
                var logger          = new TestLoggingAdapter(c);
                var timeoutStorage  = new SqlTimeoutsSource(connectionString, new MsSqlServer(), 1024);
                var timeoutsTarget  = new ASQTarget(asqConnectionString, new DelayedDeliveryTableNameProvider());
                var migrationRunner = new MigrationRunner(logger, timeoutStorage, timeoutsTarget);

                await migrationRunner.Run(DateTime.Now.AddDays(-10), EndpointFilter.SpecificEndpoint(salesEndpoint), new Dictionary <string, string>());
            }))
                          .Done(c => c.GotTheDelayedMessage)
                          .Run(TimeSpan.FromSeconds(30));

            Assert.True(context.GotTheDelayedMessage);
        }
        public async Task Can_migrate_timeouts()
        {
            var sourceEndpoint = NServiceBus.AcceptanceTesting.Customization.Conventions.EndpointNamingConvention(typeof(NHibernateSource));
            var targetEndpoint = NServiceBus.AcceptanceTesting.Customization.Conventions.EndpointNamingConvention(typeof(AsqTarget));

            using (var testSession = CreateSessionFactory().OpenSession())
            { // Explicit using scope to ensure dispose before SUT connects
                using (var testTx = testSession.BeginTransaction())
                {
                    await testSession.SaveAsync(new TimeoutEntity
                    {
                        Endpoint    = sourceEndpoint,
                        Destination = targetEndpoint,
                        SagaId      = Guid.NewGuid(),
                        Headers     = "{\"NServiceBus.EnclosedMessageTypes\": \"TimeoutMigrationTool.NHibernate.AcceptanceTests.NHibernateToAsqEndToEnd+DelayedMessage\"}",
                        State       = Encoding.UTF8.GetBytes("{}"),
                        Time        = DateTime.UtcNow.AddSeconds(15)
                    });

                    await testTx.CommitAsync();
                }
            }

            var context = await Scenario.Define <TargetContext>()
                          // Create the legacy endpoint to forward the delayed message to the reporting endpoint
                          // This is needed as ASQ stores the delayed messages at the sending endpoint until
                          // delivery is needed
                          .WithEndpoint <NHibernateSource>(b => b.CustomConfig(ec =>
            {
                var transportConfig = ec.UseTransport <AzureStorageQueueTransport>();
                transportConfig.ConnectionString(asqConnectionString);
                transportConfig.DisablePublishing();

                transportConfig.DelayedDelivery().DisableTimeoutManager();

                ec.UseSerialization <NewtonsoftSerializer>();
            }))
                          .WithEndpoint <AsqTarget>(b => b.CustomConfig(ec =>
            {
                var transportConfig = ec.UseTransport <AzureStorageQueueTransport>();
                transportConfig.ConnectionString(asqConnectionString);
                transportConfig.DisablePublishing();

                transportConfig.DelayedDelivery().DisableTimeoutManager();

                ec.UseSerialization <NewtonsoftSerializer>();
            })
                                                    .When(async(_, c) =>
            {
                var logger          = new TestLoggingAdapter(c);
                var timeoutsSource  = new NHibernateTimeoutsSource(connectionString, 512, DatabaseDialect);
                var timeoutsTarget  = new ASQTarget(asqConnectionString, new DelayedDeliveryTableNameProvider());
                var migrationRunner = new MigrationRunner(logger, timeoutsSource, timeoutsTarget);

                await migrationRunner.Run(DateTime.Now.AddDays(-10), EndpointFilter.SpecificEndpoint(sourceEndpoint), new Dictionary <string, string>());
            }))
                          .Done(c => c.GotTheDelayedMessage)
                          .Run(TimeSpan.FromSeconds(90));

            Assert.True(context.GotTheDelayedMessage);
        }