From 9d95596f87831ac33fe56c7089f03f32d556d061 Mon Sep 17 00:00:00 2001 From: Tomasz Maruszak Date: Sat, 31 Aug 2024 13:05:39 +0200 Subject: [PATCH] [Host.Outbox] Execute permission is required for Outbox #297 Signed-off-by: Tomasz Maruszak --- src/Host.Plugin.Properties.xml | 2 +- .../SqlOutboxMigrationService.cs | 16 +++++++- .../SqlOutboxRepository.cs | 38 +++---------------- .../SqlOutboxTemplate.cs | 14 ++++--- 4 files changed, 29 insertions(+), 41 deletions(-) diff --git a/src/Host.Plugin.Properties.xml b/src/Host.Plugin.Properties.xml index 3585360d..47f3efb5 100644 --- a/src/Host.Plugin.Properties.xml +++ b/src/Host.Plugin.Properties.xml @@ -4,7 +4,7 @@ - 2.5.1 + 2.5.2-rc1 \ No newline at end of file diff --git a/src/SlimMessageBus.Host.Outbox.Sql/SqlOutboxMigrationService.cs b/src/SlimMessageBus.Host.Outbox.Sql/SqlOutboxMigrationService.cs index b628dedf..dbcea818 100644 --- a/src/SlimMessageBus.Host.Outbox.Sql/SqlOutboxMigrationService.cs +++ b/src/SlimMessageBus.Host.Outbox.Sql/SqlOutboxMigrationService.cs @@ -74,9 +74,21 @@ await TryApplyMigration("20240503000000_SMB_Optimizations", -- SqlOutboxTemplate.SqlOutboxMessageDeleteSent CREATE INDEX IX_Outbox_Timestamp_DeliveryComplete_1_DeliveryAborted_0 ON {qualifiedTableName} (Timestamp) WHERE (DeliveryComplete = 1 and DeliveryAborted = 0); + + BEGIN TRY + -- SqlOutboxTemplate.SqlOutboxMessageUpdateSent + CREATE TYPE {qualifiedOutboxIdTypeName} AS TABLE (Id uniqueidentifier); + END TRY + BEGIN CATCH + -- Ignore when there is lack of permissions to create a custom type. + -- In the next migration we will drop the type see: https://github.com/zarusz/SlimMessageBus/issues/297 + END CATCH; + """, + token); - -- SqlOutboxTemplate.SqlOutboxMessageUpdateSent - CREATE TYPE {qualifiedOutboxIdTypeName} AS TABLE (Id uniqueidentifier); + await TryApplyMigration("20240831000000_SMB_RemoveOutboxIdType", + $""" + DROP TYPE IF EXISTS {qualifiedOutboxIdTypeName}; """, token); } diff --git a/src/SlimMessageBus.Host.Outbox.Sql/SqlOutboxRepository.cs b/src/SlimMessageBus.Host.Outbox.Sql/SqlOutboxRepository.cs index 60b827e5..6501bb99 100644 --- a/src/SlimMessageBus.Host.Outbox.Sql/SqlOutboxRepository.cs +++ b/src/SlimMessageBus.Host.Outbox.Sql/SqlOutboxRepository.cs @@ -21,8 +21,6 @@ public async virtual Task Save(OutboxMessage message, CancellationToken token) { await EnsureConnection(); - // ToDo: Create command template - await ExecuteNonQuery(Settings.SqlSettings.OperationRetry, _sqlTemplate.SqlOutboxMessageInsert, cmd => { cmd.Parameters.Add("@Id", SqlDbType.UniqueIdentifier).Value = message.Id; @@ -63,20 +61,11 @@ public async Task AbortDelivery(IReadOnlyCollection ids, CancellationToken await EnsureConnection(); - var table = new DataTable(); - table.Columns.Add("Id", typeof(Guid)); - foreach (var guid in ids) - { - table.Rows.Add(guid); - } - var affected = await ExecuteNonQuery(Settings.SqlSettings.OperationRetry, _sqlTemplate.SqlOutboxMessageAbortDelivery, cmd => { - var param = cmd.Parameters.Add("@Ids", SqlDbType.Structured); - param.TypeName = _sqlTemplate.OutboxIdTypeQualified; - param.Value = table; + cmd.Parameters.AddWithValue("@Ids", ToIdsString(ids)); }, token: token); @@ -95,20 +84,11 @@ public async Task UpdateToSent(IReadOnlyCollection ids, CancellationToken await EnsureConnection(); - var table = new DataTable(); - table.Columns.Add("Id", typeof(Guid)); - foreach (var guid in ids) - { - table.Rows.Add(guid); - } - var affected = await ExecuteNonQuery(Settings.SqlSettings.OperationRetry, _sqlTemplate.SqlOutboxMessageUpdateSent, cmd => { - var param = cmd.Parameters.Add("@Ids", SqlDbType.Structured); - param.TypeName = _sqlTemplate.OutboxIdTypeQualified; - param.Value = table; + cmd.Parameters.AddWithValue("@Ids", ToIdsString(ids)); }, token: token); @@ -118,6 +98,8 @@ public async Task UpdateToSent(IReadOnlyCollection ids, CancellationToken } } + private string ToIdsString(IReadOnlyCollection ids) => string.Join(_sqlTemplate.InIdsSeparator, ids); + public async Task IncrementDeliveryAttempt(IReadOnlyCollection ids, int maxDeliveryAttempts, CancellationToken token) { if (ids.Count == 0) @@ -132,21 +114,11 @@ public async Task IncrementDeliveryAttempt(IReadOnlyCollection ids, int ma await EnsureConnection(); - var table = new DataTable(); - table.Columns.Add("Id", typeof(Guid)); - foreach (var guid in ids) - { - table.Rows.Add(guid); - } - var affected = await ExecuteNonQuery(Settings.SqlSettings.OperationRetry, _sqlTemplate.SqlOutboxMessageIncrementDeliveryAttempt, cmd => { - var param = cmd.Parameters.Add("@Ids", SqlDbType.Structured); - param.TypeName = _sqlTemplate.OutboxIdTypeQualified; - param.Value = table; - + cmd.Parameters.AddWithValue("@Ids", ToIdsString(ids)); cmd.Parameters.AddWithValue("@MaxDeliveryAttempts", maxDeliveryAttempts); }, token: token); diff --git a/src/SlimMessageBus.Host.Outbox.Sql/SqlOutboxTemplate.cs b/src/SlimMessageBus.Host.Outbox.Sql/SqlOutboxTemplate.cs index b50d6fa4..60da6780 100644 --- a/src/SlimMessageBus.Host.Outbox.Sql/SqlOutboxTemplate.cs +++ b/src/SlimMessageBus.Host.Outbox.Sql/SqlOutboxTemplate.cs @@ -2,7 +2,6 @@ public class SqlOutboxTemplate { - public string OutboxIdTypeQualified { get; } public string TableNameQualified { get; } public string MigrationsTableNameQualified { get; } public string SqlOutboxMessageInsert { get; } @@ -19,9 +18,10 @@ public class SqlOutboxTemplate /// internal string SqlOutboxAllMessages { get; } + public string InIdsSeparator { get; } = "|"; + public SqlOutboxTemplate(SqlOutboxSettings settings) { - OutboxIdTypeQualified = $"[{settings.SqlSettings.DatabaseSchemaName}].[{settings.SqlSettings.DatabaseOutboxTypeName}]"; TableNameQualified = $"[{settings.SqlSettings.DatabaseSchemaName}].[{settings.SqlSettings.DatabaseTableName}]"; MigrationsTableNameQualified = $"[{settings.SqlSettings.DatabaseSchemaName}].[{settings.SqlSettings.DatabaseMigrationsTableName}]"; @@ -106,25 +106,29 @@ SELECT TOP (@BatchSize) Id ORDER BY Timestamp ASC; """; + // See https://learn.microsoft.com/en-us/sql/t-sql/functions/string-split-transact-sql?view=sql-server-ver16 + // See https://stackoverflow.com/a/47777878/1906057 + var inIdsSql = $"SELECT CONVERT(uniqueidentifier, [value]) from STRING_SPLIT(@Ids, '{InIdsSeparator}')"; + SqlOutboxMessageUpdateSent = $""" UPDATE {TableNameQualified} SET [DeliveryComplete] = 1, [DeliveryAttempt] = DeliveryAttempt + 1 - WHERE [Id] IN (SELECT [Id] from @Ids); + WHERE [Id] IN ({inIdsSql}); """; SqlOutboxMessageIncrementDeliveryAttempt = $""" UPDATE {TableNameQualified} SET [DeliveryAttempt] = DeliveryAttempt + 1, [DeliveryAborted] = CASE WHEN [DeliveryAttempt] >= @MaxDeliveryAttempts THEN 1 ELSE 0 END - WHERE [Id] IN (SELECT [Id] from @Ids); + WHERE [Id] IN ({inIdsSql}); """; SqlOutboxMessageAbortDelivery = $""" UPDATE {TableNameQualified} SET [DeliveryAttempt] = DeliveryAttempt + 1, [DeliveryAborted] = 1 - WHERE [Id] IN (SELECT [Id] from @Ids); + WHERE [Id] IN ({inIdsSql}); """; SqlOutboxMessageRenewLock = $"""