Skip to content

Commit 0db96bc

Browse files
authored
Merge pull request #4 from cajuncoding/feature/add_global_outbox_initializer_for_configuration
Feature/add global outbox initializer for configuration
2 parents b11d6e4 + 930e84a commit 0db96bc

25 files changed

+453
-71
lines changed

README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ then I do love-me-some-coffee!*
1717
<img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="41" width="174">
1818
</a>
1919

20+
## Release Notes v1.0.1:
21+
- Improved support for customizing OutboxTable Configuration and Distributed Mutex Lock settings via SqlTransactionalOutboxInitializer.Configure() initialization.
22+
2023
## Release Notes v1.0.0:
2124
- (Breaking Changes) Fully migrated (refactored) to now use `Azure.Messaging.ServiceBus` SDK/Library for future support; other Azure Service Bus libraries are all now fully deprecated by Microsoft.
2225
- The main breaking change is now the use of ServiceBusReceivedMessage vs deprecated Message object.
@@ -31,6 +34,65 @@ then I do love-me-some-coffee!*
3134
### Prior Release Notes
3235
- BETA Release v0.0.1: The library is current being shared/released in a _Beta_ form. It is being actively used for a variety of projects, and as the confidence in the functionality and stability increases through testing we will update and provide a full release. Release notes and detais will be posted here as needed.
3336

37+
## Initialization
38+
The Sql Transactional Outbox provides uses several default values that can be customized at initialization
39+
so that all the convenience methods (e.g. Sql Connection/Transaction custom extensions) work as expected with
40+
the values you need.
41+
*NOTE: This should only be done in your applications' startup/initialization (e.g. application root, Program.cs, Startup.cs, etc.).*
42+
43+
```csharp
44+
//This is the global SqlTransactionalOutbox initializer that allows configuring custom settings to be used...
45+
//NOTE: Not all values need to be specified, any values that are not specified (e.g. or are set to null)
46+
// will retain the default values.
47+
SqlTransactionalOutboxInitializer.Configure(config =>
48+
{
49+
config.WithOutboxTableConfig(new OutboxTableConfig(
50+
transactionalOutboxSchemaName: "...",
51+
transactionalOutboxTableName: "...",
52+
pkeyFieldName: "...",
53+
payloadFieldName: "...",
54+
uniqueIdentifierFieldName: "...",
55+
fifoGroupingIdentifier: "...",
56+
statusFieldName: "...",
57+
publishTargetFieldName: "...",
58+
publishAttemptsFieldName: "...",
59+
createdDateTimeUtcFieldName: "..."
60+
))
61+
.WithDistributedMutexLockSettings(
62+
lockAcquisitionTimeoutSeconds: 1,
63+
lockNamePrefix: "..."
64+
);
65+
});
66+
```
67+
68+
## Database Schema:
69+
The schema used for the SQL Server implementation is as follows. This is also stored in the project here:
70+
(SqlTransactionalOutbox.SqlServer.Common => _SqlScript => TransactionalOutboxSqlScript.sql)
71+
[https://github.com/cajuncoding/SqlTransactionalOutbox/blob/main/SqlTransactionalOutbox.SqlServer.Common/_SqlScript/TransactionalOutboxSqlScript.sql]
72+
```sql
73+
CREATE SCHEMA notifications;
74+
GO
75+
76+
--DROP TABLE [notifications].[TransactionalOutboxQueue];
77+
CREATE TABLE [notifications].[TransactionalOutboxQueue] (
78+
[Id] INT IDENTITY NOT NULL PRIMARY KEY,
79+
[UniqueIdentifier] UNIQUEIDENTIFIER NOT NULL,
80+
[FifoGroupingIdentifier] VARCHAR(200) NULL,
81+
[Status] VARCHAR(50) NOT NULL,
82+
[CreatedDateTimeUtc] DATETIME2 NOT NULL DEFAULT SysUtcDateTime(),
83+
[PublishAttempts] INT NOT NULL DEFAULT 0,
84+
[PublishTarget] VARCHAR(200) NOT NULL, -- Topic and/or Queue name
85+
[Payload] NVARCHAR(MAX), -- Generic Payload supporting Implementation specific processing (e.g. Json)
86+
);
87+
GO
88+
89+
CREATE NONCLUSTERED INDEX [IDX_TransactionalOutboxQueue_UniqueIdentifier] ON [notifications].[TransactionalOutboxQueue] ([UniqueIdentifier]);
90+
GO
91+
92+
CREATE NONCLUSTERED INDEX [IDX_TransactionalOutboxQueue_Status] ON [notifications].[TransactionalOutboxQueue] ([Status]);
93+
GO
94+
```
95+
3496
## Documentation TODOs:
3597
Provide documentation for:
3698
- Transactional Outbox Pattern summary/overview

SqlTransactionalOutbox.AzureServiceBus/SqlTransactionalOutbox.AzureServiceBus.csproj

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<PropertyGroup>
44
<TargetFramework>netstandard2.1</TargetFramework>
55
<RootNamespace>SqlTransactionalOutbox.AzureServiceBus</RootNamespace>
6-
<Version>1.0.0</Version>
6+
<Version>1.0.1</Version>
77
<Authors>BBernard / CajunCoding</Authors>
88
<Copyright>Copyright © 2021</Copyright>
99
<Description>The Azure Service Bus publishing/messaging &amp; receiving implementation of the SqlTransactionalOutbox framework.
@@ -14,7 +14,11 @@ A lightweight library &amp; framework for implementing the Transactional Outbox
1414
<PackageProjectUrl>https://github.com/cajuncoding/SqlTransactionalOutbox</PackageProjectUrl>
1515
<RepositoryUrl>https://github.com/cajuncoding/SqlTransactionalOutbox</RepositoryUrl>
1616
<PackageTags>sql server sqlserver sql-server transactional outbox transactional-outbox outbox-pattern microservice eventservices event-services event-notifications azure service bus azurefunctions azure-functions</PackageTags>
17-
<PackageReleaseNotes>Release Notes:
17+
<PackageReleaseNotes>
18+
Release Notes:
19+
- Bump version along with SqlTransactionalOutbox.Common.
20+
21+
Prior Release Notes:
1822
- (Breaking Changes) Fully migrated to Azure.Messaging.ServiceBus SDK/Library for future support; other AzureServiceBus libraries are all fully deprecated by Microsoft.
1923
- The main breaking change is now the use of ServiceBusReceivedMessage vs deprecated Message object.
2024
- All Interfaces and the genearl abstraction are still valid so code updates are straightforward.
@@ -24,8 +28,6 @@ A lightweight library &amp; framework for implementing the Transactional Outbox
2428
- Also added a new Default implementation for AsyncThreadOutboxProcessingAgent (to run the Processing in an async Thread; ideal for AspNet Applications).
2529
- Improved Json serialization to eliminate unnecessary storing of Null properties and consistently use camelCase Json.
2630
- Added full Console Sample Application (in Github Source) that provides Demo of the full lifecycle of the Sql Transactional Outbox.
27-
28-
Prior Release Notes:
2931
- Bump version of SqlTransactionalOutbox.Common.
3032
- Initial BETA release for default implementations of Azure Service Bus publishing &amp; receiving of transactional outbox items.
3133
</PackageReleaseNotes>

SqlTransactionalOutbox.Common/OutboxProcessingOptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public class OutboxProcessingOptions
4444
/// <summary>
4545
/// Determine if we should enforce FIFO Publishing order which requires the use of
4646
/// a distributed application mutex lock to ensure that only one processor can execute
47-
/// at any time -- eliminating risk of parallel and potential impacts to processing order.
47+
/// at any time -- eliminating risk of parallel processing and other potential impacts to processing order.
4848
/// </summary>
4949
public bool FifoEnforcedPublishingEnabled { get; set; } = false;
5050

SqlTransactionalOutbox.Common/OutboxTableConfig.cs

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,61 @@ namespace SqlTransactionalOutbox
66
{
77
public class OutboxTableConfig : ISqlTransactionalOutboxTableConfig
88
{
9+
/// <summary>
10+
/// Configuration for the Outbox Database Table; any values not specified or set to null will use Default values.
11+
/// </summary>
12+
/// <param name="transactionalOutboxSchemaName"></param>
13+
/// <param name="transactionalOutboxTableName"></param>
14+
/// <param name="pkeyFieldName"></param>
15+
/// <param name="uniqueIdentifierFieldName"></param>
16+
/// <param name="fifoGroupingIdentifier"></param>
17+
/// <param name="statusFieldName"></param>
18+
/// <param name="publishTargetFieldName"></param>
19+
/// <param name="payloadFieldName"></param>
20+
/// <param name="publishAttemptsFieldName"></param>
21+
/// <param name="createdDateTimeUtcFieldName"></param>
22+
public OutboxTableConfig(
23+
string transactionalOutboxSchemaName = null,
24+
string transactionalOutboxTableName = null,
25+
string pkeyFieldName = null,
26+
string uniqueIdentifierFieldName = null,
27+
string fifoGroupingIdentifier = null,
28+
string statusFieldName = null,
29+
string publishTargetFieldName = null,
30+
string payloadFieldName = null,
31+
string publishAttemptsFieldName = null,
32+
string createdDateTimeUtcFieldName = null
33+
)
34+
{
35+
TransactionalOutboxSchemaName = transactionalOutboxSchemaName ?? DefaultTransactionalOutboxSchemaName;
36+
TransactionalOutboxTableName = transactionalOutboxTableName ?? DefaultTransactionalOutboxTableName;
37+
38+
PKeyFieldName = pkeyFieldName ?? DefaultPKeyFieldName;
39+
UniqueIdentifierFieldName = uniqueIdentifierFieldName ?? nameof(OutboxProcessingItem<Guid>.UniqueIdentifier);
40+
FifoGroupingIdentifier = fifoGroupingIdentifier ?? nameof(OutboxProcessingItem<Guid>.FifoGroupingIdentifier);
41+
StatusFieldName = statusFieldName ?? nameof(OutboxProcessingItem<Guid>.Status);
42+
PublishTargetFieldName = publishTargetFieldName ?? nameof(OutboxProcessingItem<Guid>.PublishTarget);
43+
PayloadFieldName = payloadFieldName ?? nameof(OutboxProcessingItem<Guid>.Payload);
44+
PublishAttemptsFieldName = publishAttemptsFieldName ?? nameof(OutboxProcessingItem<Guid>.PublishAttempts);
45+
CreatedDateTimeUtcFieldName = createdDateTimeUtcFieldName ?? nameof(OutboxProcessingItem<Guid>.CreatedDateTimeUtc);
46+
}
47+
948
public const string DefaultTransactionalOutboxSchemaName = "notifications";
1049
public const string DefaultTransactionalOutboxTableName = "TransactionalOutboxQueue";
1150
public const string DefaultPKeyFieldName = "Id";
1251

13-
public virtual string TransactionalOutboxSchemaName { get; } = DefaultTransactionalOutboxSchemaName;
14-
public virtual string TransactionalOutboxTableName { get; } = DefaultTransactionalOutboxTableName;
52+
public string TransactionalOutboxSchemaName { get; protected set; }
53+
public string TransactionalOutboxTableName { get; protected set; }
1554

1655
//NOTE: The PKey Field is only used for Sql Server specific resolution of DateTime sort collisions & Sorting,
1756
// but is otherwise not needed for Outbox Item Model.
18-
public virtual string PKeyFieldName { get; } = DefaultPKeyFieldName;
19-
public virtual string UniqueIdentifierFieldName { get; } = nameof(OutboxProcessingItem<Guid>.UniqueIdentifier);
20-
public virtual string FifoGroupingIdentifier { get; } = nameof(OutboxProcessingItem<Guid>.FifoGroupingIdentifier);
21-
public virtual string StatusFieldName { get; } = nameof(OutboxProcessingItem<Guid>.Status);
22-
public virtual string PublishTargetFieldName { get; } = nameof(OutboxProcessingItem<Guid>.PublishTarget);
23-
public virtual string PayloadFieldName { get; } = nameof(OutboxProcessingItem<Guid>.Payload);
24-
public virtual string PublishAttemptsFieldName { get; } = nameof(OutboxProcessingItem<Guid>.PublishAttempts);
25-
public virtual string CreatedDateTimeUtcFieldName { get; } = nameof(OutboxProcessingItem<Guid>.CreatedDateTimeUtc);
57+
public string PKeyFieldName { get; protected set; }
58+
public string UniqueIdentifierFieldName { get; protected set; }
59+
public string FifoGroupingIdentifier { get; protected set; }
60+
public string StatusFieldName { get; protected set; }
61+
public string PublishTargetFieldName { get; protected set; }
62+
public string PayloadFieldName { get; protected set; }
63+
public string PublishAttemptsFieldName { get; protected set; }
64+
public string CreatedDateTimeUtcFieldName { get; protected set; }
2665
}
2766
}

SqlTransactionalOutbox.Common/SqlTransactionalOutbox.Common.csproj

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<PropertyGroup>
44
<TargetFramework>netstandard2.1</TargetFramework>
55
<RootNamespace>SqlTransactionalOutbox</RootNamespace>
6-
<Version>1.0.0</Version>
6+
<Version>1.0.1</Version>
77
<Authors>BBernard / CajunCoding</Authors>
88
<Copyright>Copyright © 2021</Copyright>
99
<Description>The Common interfaces and base/shared components for the SqlTransactionalOutbox framework.
@@ -14,7 +14,11 @@ A lightweight library &amp; framework for implementing the Transactional Outbox
1414
<PackageProjectUrl>https://github.com/cajuncoding/SqlTransactionalOutbox</PackageProjectUrl>
1515
<RepositoryUrl>https://github.com/cajuncoding/SqlTransactionalOutbox</RepositoryUrl>
1616
<PackageTags>sql server sqlserver sql-server transactional outbox transactional-outbox outbox-pattern microservice eventservices event-services event-notifications azure service bus azurefunctions azure-functions</PackageTags>
17-
<PackageReleaseNotes>Release Notes:
17+
<PackageReleaseNotes>
18+
Release Notes:
19+
- Improved support for customizing OutboxTable Configuration and Distributed Mutex Lock settings via SqlTransactionalOutboxInitializer.Configure() initialization.
20+
21+
Prior Release Notes:
1822
- (Breaking Changes) Fully migrated to Azure.Messaging.ServiceBus SDK/Library for future support; other AzureServiceBus libraries are all fully deprecated by Microsoft.
1923
- The main breaking change is now the use of ServiceBusReceivedMessage vs deprecated Message object.
2024
- All Interfaces and the genearl abstraction are still valid so code updates are straightforward.
@@ -24,8 +28,6 @@ A lightweight library &amp; framework for implementing the Transactional Outbox
2428
- Also added a new Default implementation for AsyncThreadOutboxProcessingAgent (to run the Processing in an async Thread; ideal for AspNet Applications).
2529
- Improved Json serialization to eliminate unnecessary storing of Null properties and consistently use camelCase Json.
2630
- Added full Console Sample Application (in Github Source) that provides Demo of the full lifecycle of the Sql Transactional Outbox.
27-
28-
Prior Release Notes:
2931
- Add Convenience Method for PayloadBuilder.FromObject().
3032
- Add lazy loaded convenience property ParsedBody to OutboxReceivedItem.
3133
- Initial BETA release for core interfaces and shared components; supports default implementations of SQL Server transactional outbox &amp; Azure Service Bus publishing.
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
using System;
2+
using System.Runtime.CompilerServices;
3+
using SqlTransactionalOutbox.CustomExtensions;
4+
5+
namespace SqlTransactionalOutbox
6+
{
7+
public static class SqlTransactionalOutboxDefaults
8+
{
9+
static SqlTransactionalOutboxDefaults()
10+
{
11+
//Initialize all Default Values!
12+
//NOTE: We use the Reset method for consistency while enabling re-use via Unit Tests, etc. so that the State can be reset at any time!
13+
SqlTransactionalOutboxInitializer.Configure(c => c.ResetToDefaults());
14+
}
15+
16+
public static ISqlTransactionalOutboxTableConfig OutboxTableConfig { get; internal set; }
17+
public static int DistributedMutexAcquisitionTimeoutSeconds { get; internal set; }
18+
public static string DistributeMutexLockPrefix { get; internal set; }
19+
}
20+
21+
public class SqlTransactionalOutboxInitializer
22+
{
23+
private static readonly object _padLock = new object();
24+
25+
public static SqlTransactionalOutboxInitializer Configure(Action<ConfigBuilder> configAction)
26+
{
27+
lock (_padLock)
28+
{
29+
configAction?.Invoke(new ConfigBuilder());
30+
}
31+
32+
// Does nothing currently but may in the future...
33+
return new SqlTransactionalOutboxInitializer();
34+
}
35+
36+
public class ConfigBuilder
37+
{
38+
/// <summary>
39+
/// Prevent external construction to enforce our Configuration Syntax and protect future enhancements with less risk of breaking changes...
40+
/// </summary>
41+
internal ConfigBuilder()
42+
{ }
43+
44+
/// <summary>
45+
/// Initialize/Reset all configuration values to original Default values.
46+
/// </summary>
47+
/// <returns></returns>
48+
public ConfigBuilder ResetToDefaults()
49+
{
50+
return this
51+
.WithOutboxTableConfig(new OutboxTableConfig())
52+
.WithDistributedMutexLockSettings(
53+
lockNamePrefix: "SqlServerTransactionalOutboxProcessor::",
54+
lockAcquisitionTimeoutSeconds: 1
55+
);
56+
}
57+
58+
/// <summary>
59+
/// Initialize the global default settings for the OutboxTableConfig which will be supported by all convenience methods (e.g. Sql Custom Extensions)!
60+
/// NOTE: This should ONLY be called once at application startup and any thread concerns must be manually controlled by the calling code!
61+
/// </summary>
62+
/// <param name="customConfig"></param>
63+
public ConfigBuilder WithOutboxTableConfig(ISqlTransactionalOutboxTableConfig customConfig)
64+
{
65+
SqlTransactionalOutboxDefaults.OutboxTableConfig = customConfig.AssertNotNull(nameof(customConfig));
66+
return this;
67+
}
68+
69+
/// <summary>
70+
/// Initialize the global default settings for the Distributed Mutex settings which will be supported by all convenience methods (e.g. Sql Custom Extensions)!
71+
/// NOTE: This should ONLY be called once at application startup and any thread concerns must be manually controlled by the calling code!
72+
/// </summary>
73+
/// <param name="lockAcquisitionTimeoutSeconds"></param>
74+
/// <param name="lockNamePrefix"></param>
75+
public ConfigBuilder WithDistributedMutexLockSettings(
76+
int? lockAcquisitionTimeoutSeconds = null,
77+
string lockNamePrefix = null
78+
)
79+
{
80+
if (lockAcquisitionTimeoutSeconds < 0)
81+
throw new ArgumentOutOfRangeException(nameof(lockAcquisitionTimeoutSeconds), "Lock acquisition timeout must be 0 or greater.");
82+
else if (lockAcquisitionTimeoutSeconds.HasValue)
83+
SqlTransactionalOutboxDefaults.DistributedMutexAcquisitionTimeoutSeconds = (int)lockAcquisitionTimeoutSeconds;
84+
85+
//Though NOT Advised, for flexibility we Allow the client to set the Prefix to anything (event empty string) if they choose...
86+
if (lockNamePrefix != null)
87+
SqlTransactionalOutboxDefaults.DistributeMutexLockPrefix = lockNamePrefix;
88+
89+
return this;
90+
}
91+
}
92+
}
93+
}

0 commit comments

Comments
 (0)