diff --git a/Blumchen.sln b/Blumchen.sln
index a5020ed..f8510ef 100644
--- a/Blumchen.sln
+++ b/Blumchen.sln
@@ -43,6 +43,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "postgres", "postgres", "{8A
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "demo", "demo", "{A4044484-FE08-4399-8239-14AABFA30AD7}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SubscriberWorker", "src\SubscriberWorker\SubscriberWorker.csproj", "{DB58DB36-0366-4ABA-BC06-FCA9BB10EB92}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -69,6 +71,10 @@ Global
{2BBDA071-FB1C-4D62-A954-B22EA6B1C738}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2BBDA071-FB1C-4D62-A954-B22EA6B1C738}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2BBDA071-FB1C-4D62-A954-B22EA6B1C738}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DB58DB36-0366-4ABA-BC06-FCA9BB10EB92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DB58DB36-0366-4ABA-BC06-FCA9BB10EB92}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DB58DB36-0366-4ABA-BC06-FCA9BB10EB92}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DB58DB36-0366-4ABA-BC06-FCA9BB10EB92}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -79,6 +85,7 @@ Global
{F81E2D5B-FC59-4396-A911-56BE65E4FE80} = {A4044484-FE08-4399-8239-14AABFA30AD7}
{C050E9E8-3FB6-4581-953F-31826E385FB4} = {CD59A1A0-F40D-4047-87A3-66C0F1519FA5}
{8AAAA344-B5FD-48D9-B2BA-379E374448D4} = {CD59A1A0-F40D-4047-87A3-66C0F1519FA5}
+ {DB58DB36-0366-4ABA-BC06-FCA9BB10EB92} = {A4044484-FE08-4399-8239-14AABFA30AD7}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9A868C51-0460-4700-AF33-E1A921192614}
diff --git a/README.md b/README.md
index 48856b2..36fd616 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ Main logic is placed in [EventsSubscription](./src/Blumchen/Subscriptions/Subscr
```shell
docker-compose up
```
-2. Run(order doesn't matter) Publisher and Subscriber apps, under 'demo' folder, from vs-studio, and follow Publisher instructions.
+2. Run(order doesn't matter) Publisher and (Subscriber or SubscriberWorker) apps, under 'demo' folder, from vs-studio, and follow Publisher instructions.
## Testing (against default docker instance)
diff --git a/src/Blumchen.DependencyInjection/Blumchen.DependencyInjection.csproj b/src/Blumchen.DependencyInjection/Blumchen.DependencyInjection.csproj
new file mode 100644
index 0000000..5c7b408
--- /dev/null
+++ b/src/Blumchen.DependencyInjection/Blumchen.DependencyInjection.csproj
@@ -0,0 +1,54 @@
+
+
+
+ 0.1.1
+ net8.0
+ true
+ true
+ true
+ false
+ true
+ true
+ true
+ true
+ 12.0
+ Oskar Dudycz
+
+ https://github.com/event-driven-io/Blumchen
+ MIT
+ https://github.com/event-driven-io/Blumchen.git
+ true
+ Blumchen
+ true
+ true
+ true
+ snupkg
+ Blumchen
+ true
+ enable
+ enable
+
+
+
+ 1591
+
+
+
+ 1591
+
+
+
+
+ all
+ none
+ all
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Blumchen/Blumchen.csproj b/src/Blumchen/Blumchen.csproj
index 4e6b52c..0138080 100644
--- a/src/Blumchen/Blumchen.csproj
+++ b/src/Blumchen/Blumchen.csproj
@@ -1,7 +1,7 @@
- 0.1.0
+ 0.1.1
net8.0
true
true
@@ -25,20 +25,35 @@
snupkg
Blumchen
+
+
+ 1591
+
+
+
+
+ 1591
+
+
<_Parameter1>Tests
+
+ <_Parameter1>Blumchen.DependencyInjection
+
-
+
all
none
all
+
+
diff --git a/src/Blumchen/Database/Run.cs b/src/Blumchen/Database/Run.cs
index 4e03b17..f4ccec1 100644
--- a/src/Blumchen/Database/Run.cs
+++ b/src/Blumchen/Database/Run.cs
@@ -4,8 +4,6 @@
using Blumchen.Subscriptions.ReplicationMessageHandlers;
using Npgsql;
-#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
-
namespace Blumchen.Database;
public static class Run
diff --git a/src/Blumchen/DependencyInjection/ServiceCollectionExtensions.cs b/src/Blumchen/DependencyInjection/ServiceCollectionExtensions.cs
new file mode 100644
index 0000000..c01d5ab
--- /dev/null
+++ b/src/Blumchen/DependencyInjection/ServiceCollectionExtensions.cs
@@ -0,0 +1,23 @@
+using Blumchen.Subscriptions;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+#pragma warning disable IL2091
+
+namespace Blumchen.DependencyInjection;
+
+public static class ServiceCollectionExtensions
+{
+
+ public static IServiceCollection AddBlumchen(
+ this IServiceCollection service,
+ Func workerOptions)
+ where T : class, IHandler =>
+ service
+ .AddKeyedSingleton(typeof(T), (provider, _) => workerOptions(provider, new WorkerOptionsBuilder()).Build())
+ .AddHostedService(provider =>
+ new Worker(workerOptions(provider, new WorkerOptionsBuilder()).Build(),
+ ServiceProviderServiceExtensions.GetRequiredService>>(provider)));
+
+
+}
diff --git a/src/Blumchen/DependencyInjection/Worker.cs b/src/Blumchen/DependencyInjection/Worker.cs
new file mode 100644
index 0000000..4f137b7
--- /dev/null
+++ b/src/Blumchen/DependencyInjection/Worker.cs
@@ -0,0 +1,41 @@
+using System.Collections.Concurrent;
+using Blumchen.Subscriptions;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+
+namespace Blumchen.DependencyInjection;
+
+public class Worker(
+ WorkerOptions options,
+ ILogger> logger): BackgroundService where T : class, IHandler
+{
+ private string WorkerName { get; } = $"{nameof(Worker)}<{typeof(T).Name}>";
+ private static readonly ConcurrentDictionary> LoggingActions = new(StringComparer.OrdinalIgnoreCase);
+ private static void Notify(ILogger logger, LogLevel level, string template, params object[] parameters)
+ {
+ static Action LoggerAction(LogLevel ll, bool enabled) =>
+ (ll, enabled) switch
+ {
+ (LogLevel.Information, true) => (logger, template, parameters) => logger.LogInformation(template, parameters),
+ (LogLevel.Debug, true) => (logger, template, parameters) => logger.LogDebug(template, parameters),
+ (_, _) => (_, _, _) => { }
+ };
+ LoggingActions.GetOrAdd(template,_ => LoggerAction(level, logger.IsEnabled(level)))(logger, template, parameters);
+ }
+
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ await options.ResiliencePipeline.ExecuteAsync(async token =>
+ {
+ await using var subscription = new Subscription();
+ await using var cursor = subscription.Subscribe(options.SubscriptionOptions, ct: token)
+ .GetAsyncEnumerator(token);
+ Notify(logger, LogLevel.Information,"{WorkerName} started", WorkerName);
+ while (await cursor.MoveNextAsync().ConfigureAwait(false) && !token.IsCancellationRequested)
+ Notify(logger, LogLevel.Debug, "{cursor.Current} processed", cursor.Current);
+
+ }, stoppingToken).ConfigureAwait(false);
+ Notify(logger, LogLevel.Information, "{WorkerName} stopped", WorkerName);
+ }
+
+}
diff --git a/src/Blumchen/DependencyInjection/WorkerOptionsBuilder.cs b/src/Blumchen/DependencyInjection/WorkerOptionsBuilder.cs
new file mode 100644
index 0000000..07c79c5
--- /dev/null
+++ b/src/Blumchen/DependencyInjection/WorkerOptionsBuilder.cs
@@ -0,0 +1,37 @@
+using Blumchen.Subscriptions;
+using Polly;
+
+namespace Blumchen.DependencyInjection;
+
+public record WorkerOptions(ResiliencePipeline ResiliencePipeline, ISubscriptionOptions SubscriptionOptions);
+
+public interface IWorkerOptionsBuilder
+{
+ IWorkerOptionsBuilder ResiliencyPipeline(ResiliencePipeline resiliencePipeline);
+ IWorkerOptionsBuilder Subscription(Func? builder);
+ WorkerOptions Build();
+}
+
+internal sealed class WorkerOptionsBuilder: IWorkerOptionsBuilder
+{
+ private ResiliencePipeline? _resiliencePipeline = default;
+ private Func? _builder;
+
+ public IWorkerOptionsBuilder ResiliencyPipeline(ResiliencePipeline resiliencePipeline)
+ {
+ _resiliencePipeline = resiliencePipeline;
+ return this;
+ }public IWorkerOptionsBuilder Subscription(Func? builder)
+ {
+ _builder = builder;
+ return this;
+ }
+
+ public WorkerOptions Build()
+ {
+ ArgumentNullException.ThrowIfNull(_resiliencePipeline);
+ ArgumentNullException.ThrowIfNull(_builder);
+ return new(_resiliencePipeline, _builder(new SubscriptionOptionsBuilder()).Build());
+ }
+}
+
diff --git a/src/Blumchen/MessageTableOptions.cs b/src/Blumchen/MessageTableOptions.cs
index a7fe0b1..ec91b9b 100644
--- a/src/Blumchen/MessageTableOptions.cs
+++ b/src/Blumchen/MessageTableOptions.cs
@@ -1,9 +1,9 @@
using Blumchen.Subscriptions;
+using JetBrains.Annotations;
using NpgsqlTypes;
namespace Blumchen;
-#pragma warning disable CS1591
public record TableDescriptorBuilder
{
private MessageTable TableDescriptor { get; set; } = new();
@@ -34,6 +34,9 @@ public TableDescriptorBuilder MessageType(string name, int dimension = 250)
return this;
}
+ [UsedImplicitly]
+ public TableDescriptorBuilder UseDefaults() => this;
+
public record MessageTable(string Name = MessageTable.DefaultName)
{
internal const string DefaultName = "outbox";
diff --git a/src/Blumchen/Publications/MessageAppender.cs b/src/Blumchen/Publications/MessageAppender.cs
index 623c8b4..860d80b 100644
--- a/src/Blumchen/Publications/MessageAppender.cs
+++ b/src/Blumchen/Publications/MessageAppender.cs
@@ -3,12 +3,11 @@
using Npgsql;
namespace Blumchen.Publications;
-#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public static class MessageAppender
{
public static async Task AppendAsync(T @input
- , (TableDescriptorBuilder.MessageTable tableDescriptor, IJsonTypeResolver jsonTypeResolver) resolver
+ , PublisherOptions resolver
, NpgsqlConnection connection
, NpgsqlTransaction transaction
, CancellationToken ct
@@ -19,10 +18,10 @@ public static async Task AppendAsync(T @input
case null:
throw new ArgumentNullException(nameof(@input));
case IEnumerable inputs:
- await AppendBatchAsyncOfT(inputs, resolver.tableDescriptor, resolver.jsonTypeResolver, connection, transaction, ct).ConfigureAwait(false);
+ await AppendBatchAsyncOfT(inputs, resolver.TableDescriptor, resolver.JsonTypeResolver, connection, transaction, ct).ConfigureAwait(false);
break;
default:
- await AppendAsyncOfT(input, resolver.tableDescriptor, resolver.jsonTypeResolver, connection, transaction, ct).ConfigureAwait(false);
+ await AppendAsyncOfT(input, resolver.TableDescriptor, resolver.JsonTypeResolver, connection, transaction, ct).ConfigureAwait(false);
break;
}
}
@@ -36,30 +35,32 @@ private static async Task AppendAsyncOfT(T input
{
var (typeName, jsonTypeInfo) = typeResolver.Resolve(typeof(T));
var data = JsonSerialization.ToJson(@input, jsonTypeInfo);
- var command = new NpgsqlCommand(
+
+ await using var command = new NpgsqlCommand(
$"INSERT INTO {tableDescriptor.Name}({tableDescriptor.MessageType.Name}, {tableDescriptor.Data.Name}) values ('{typeName}', '{data}')",
connection,
transaction
);
+ await command.PrepareAsync(ct).ConfigureAwait(false);
await command.ExecuteNonQueryAsync(ct).ConfigureAwait(false);
}
public static async Task AppendAsync(T input
- , (TableDescriptorBuilder.MessageTable tableDescriptor, IJsonTypeResolver resolver) options
+ , PublisherOptions options
, string connectionString
, CancellationToken ct)
where T: class
{
var type = typeof(T);
- var (typeName, jsonTypeInfo) = options.resolver.Resolve(type);
+ var (typeName, jsonTypeInfo) = options.JsonTypeResolver.Resolve(type);
var data = JsonSerialization.ToJson(input, jsonTypeInfo);
- var connection = new NpgsqlConnection(connectionString);
- await using var connection1 = connection.ConfigureAwait(false);
+ await using var connection = new NpgsqlConnection(connectionString);
await connection.OpenAsync(ct).ConfigureAwait(false);
- var command = connection.CreateCommand();
+ await using var command = connection.CreateCommand();
command.CommandText =
- $"INSERT INTO {options.tableDescriptor.Name}({options.tableDescriptor.MessageType.Name}, {options.tableDescriptor.Data.Name}) values ('{typeName}', '{data}')";
+ $"INSERT INTO {options.TableDescriptor.Name}({options.TableDescriptor.MessageType.Name}, {options.TableDescriptor.Data.Name}) values ('{typeName}', '{data}')";
+ await command.PrepareAsync(ct).ConfigureAwait(false);
await command.ExecuteNonQueryAsync(ct).ConfigureAwait(false);
}
@@ -70,7 +71,7 @@ private static async Task AppendBatchAsyncOfT(T inputs
, NpgsqlTransaction transaction
, CancellationToken ct) where T : class, IEnumerable
{
- var batch = new NpgsqlBatch(connection, transaction);
+ await using var batch = new NpgsqlBatch(connection, transaction);
foreach (var input in inputs)
{
var (typeName, jsonTypeInfo) = resolver.Resolve(input.GetType());
diff --git a/src/Blumchen/Publications/PublisherOptions.cs b/src/Blumchen/Publications/PublisherOptions.cs
new file mode 100644
index 0000000..d25c048
--- /dev/null
+++ b/src/Blumchen/Publications/PublisherOptions.cs
@@ -0,0 +1,20 @@
+using Blumchen.Database;
+using Blumchen.Serialization;
+using Npgsql;
+
+namespace Blumchen.Publications;
+
+public record PublisherOptions(TableDescriptorBuilder.MessageTable TableDescriptor, IJsonTypeResolver JsonTypeResolver);
+
+public static class PublisherOptionsExtensions
+{
+ public static async Task EnsureTable(this PublisherOptions publisherOptions, NpgsqlDataSource dataSource, CancellationToken ct)
+ {
+ await dataSource.EnsureTableExists(publisherOptions.TableDescriptor, ct);
+ return publisherOptions;
+ }
+
+ public static Task EnsureTable(this PublisherOptions publisherOptions,
+ string connectionString, CancellationToken ct)
+ => EnsureTable(publisherOptions, new NpgsqlDataSourceBuilder(connectionString).Build(), ct);
+}
diff --git a/src/Blumchen/Publications/PublisherSetupOptionsBuilder.cs b/src/Blumchen/Publications/PublisherSetupOptionsBuilder.cs
index 90e7792..e42c4a2 100644
--- a/src/Blumchen/Publications/PublisherSetupOptionsBuilder.cs
+++ b/src/Blumchen/Publications/PublisherSetupOptionsBuilder.cs
@@ -5,7 +5,6 @@
namespace Blumchen.Publications;
-#pragma warning disable CS1591
public class PublisherSetupOptionsBuilder
{
private INamingPolicy? _namingPolicy;
@@ -34,7 +33,7 @@ public PublisherSetupOptionsBuilder WithTable(Func
{
@@ -25,8 +24,8 @@ internal sealed class JsonTypeResolver(
internal void WhiteList(Type type)
{
var typeInfo = SerializationContext.GetTypeInfo(type) ?? throw new NotSupportedException(type.FullName);
- _typeDictionary.AddOrUpdate(_namingPolicy.Bind(typeInfo.Type), _ => typeInfo.Type, (s,t) =>typeInfo.Type);
- _typeInfoDictionary.AddOrUpdate(typeInfo.Type, _ => typeInfo, (_,__)=> typeInfo);
+ _typeDictionary.AddOrUpdate(_namingPolicy.Bind(typeInfo.Type), _ => typeInfo.Type, (_,_) =>typeInfo.Type);
+ _typeInfoDictionary.AddOrUpdate(typeInfo.Type, _ => typeInfo, (_,_)=> typeInfo);
}
public (string, JsonTypeInfo) Resolve(Type type) =>
diff --git a/src/Blumchen/Serialization/JsonSerialization.cs b/src/Blumchen/Serialization/JsonSerialization.cs
index feb4095..6fdfad0 100644
--- a/src/Blumchen/Serialization/JsonSerialization.cs
+++ b/src/Blumchen/Serialization/JsonSerialization.cs
@@ -4,7 +4,6 @@
using Blumchen.Streams;
namespace Blumchen.Serialization;
-#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public static class JsonSerialization
{
diff --git a/src/Blumchen/Serialization/MessageUrnAttribute.cs b/src/Blumchen/Serialization/MessageUrnAttribute.cs
index 8963e34..11ae685 100644
--- a/src/Blumchen/Serialization/MessageUrnAttribute.cs
+++ b/src/Blumchen/Serialization/MessageUrnAttribute.cs
@@ -1,7 +1,6 @@
using System.Collections.Concurrent;
namespace Blumchen.Serialization;
-#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)]
public class MessageUrnAttribute:
diff --git a/src/Blumchen/Subscriptions/IConsume.cs b/src/Blumchen/Subscriptions/IConsume.cs
deleted file mode 100644
index 4a59c0f..0000000
--- a/src/Blumchen/Subscriptions/IConsume.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Blumchen.Subscriptions;
-#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
-
-public interface IConsume;
-
-public interface IConsumes: IConsume where T : class
-{
- Task Handle(T value);
-}
diff --git a/src/Blumchen/Subscriptions/IErrorProcessor.cs b/src/Blumchen/Subscriptions/IErrorProcessor.cs
new file mode 100644
index 0000000..b3cec9d
--- /dev/null
+++ b/src/Blumchen/Subscriptions/IErrorProcessor.cs
@@ -0,0 +1,11 @@
+namespace Blumchen.Subscriptions;
+
+public interface IErrorProcessor
+{
+ Func Process { get; }
+}
+
+public record ConsoleOutErrorProcessor: IErrorProcessor
+{
+ public Func Process => exception => Console.Out.WriteLineAsync($"record id:{0} resulted in error:{exception.Message}");
+}
diff --git a/src/Blumchen/Subscriptions/IHandler.cs b/src/Blumchen/Subscriptions/IHandler.cs
new file mode 100644
index 0000000..b722f25
--- /dev/null
+++ b/src/Blumchen/Subscriptions/IHandler.cs
@@ -0,0 +1,8 @@
+namespace Blumchen.Subscriptions;
+
+public interface IHandler;
+
+public interface IHandler: IHandler where T : class
+{
+ Task Handle(T value);
+}
diff --git a/src/Blumchen/Subscriptions/ISubscriptionOptions.cs b/src/Blumchen/Subscriptions/ISubscriptionOptions.cs
index 1be177c..27dd513 100644
--- a/src/Blumchen/Subscriptions/ISubscriptionOptions.cs
+++ b/src/Blumchen/Subscriptions/ISubscriptionOptions.cs
@@ -1,31 +1,35 @@
using Blumchen.Subscriptions.Replication;
using JetBrains.Annotations;
+using Npgsql;
using static Blumchen.Subscriptions.Management.PublicationManagement;
using static Blumchen.Subscriptions.Management.ReplicationSlotManagement;
namespace Blumchen.Subscriptions;
-internal interface ISubscriptionOptions
+public interface ISubscriptionOptions
{
- [UsedImplicitly] string ConnectionString { get; }
+ [UsedImplicitly] NpgsqlDataSource DataSource { get; }
+ [UsedImplicitly] NpgsqlConnectionStringBuilder ConnectionStringBuilder { get; }
IReplicationDataMapper DataMapper { get; }
[UsedImplicitly] PublicationSetupOptions PublicationOptions { get; }
[UsedImplicitly] ReplicationSlotSetupOptions ReplicationOptions { get; }
[UsedImplicitly] IErrorProcessor ErrorProcessor { get; }
void Deconstruct(
- out string connectionString,
+ out NpgsqlDataSource dataSource,
+ out NpgsqlConnectionStringBuilder connectionStringBuilder,
out PublicationSetupOptions publicationSetupOptions,
out ReplicationSlotSetupOptions replicationSlotSetupOptions,
out IErrorProcessor errorProcessor,
out IReplicationDataMapper dataMapper,
- out Dictionary registry);
+ out Dictionary registry);
}
internal record SubscriptionOptions(
- string ConnectionString,
+ NpgsqlDataSource DataSource,
+ NpgsqlConnectionStringBuilder ConnectionStringBuilder,
PublicationSetupOptions PublicationOptions,
ReplicationSlotSetupOptions ReplicationOptions,
IErrorProcessor ErrorProcessor,
IReplicationDataMapper DataMapper,
- Dictionary Registry): ISubscriptionOptions;
+ Dictionary Registry): ISubscriptionOptions;
diff --git a/src/Blumchen/Subscriptions/Management/PublicationManagement.cs b/src/Blumchen/Subscriptions/Management/PublicationManagement.cs
index 64e6033..c2b6f6c 100644
--- a/src/Blumchen/Subscriptions/Management/PublicationManagement.cs
+++ b/src/Blumchen/Subscriptions/Management/PublicationManagement.cs
@@ -3,7 +3,6 @@
using Npgsql;
#pragma warning disable CA2208
-#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
namespace Blumchen.Subscriptions.Management;
@@ -70,14 +69,11 @@ internal static Task CreatePublication(
ISet eventTypes,
CancellationToken ct
) {
+ var sql = $"CREATE PUBLICATION \"{publicationName}\" FOR TABLE {tableName} {{0}} WITH (publish = 'insert');";
return eventTypes.Count switch
{
- 0 => Execute(dataSource, $"CREATE PUBLICATION {publicationName} FOR TABLE {tableName} WITH (publish = 'insert');",
- ct
- ),
- _ => Execute(dataSource, $"CREATE PUBLICATION {publicationName} FOR TABLE {tableName} WHERE ({PublicationFilter(eventTypes)}) WITH (publish = 'insert');",
- ct
- )
+ 0 => Execute(dataSource, string.Format(sql,string.Empty), ct),
+ _ => Execute(dataSource, string.Format(sql, $"WHERE ({PublicationFilter(eventTypes)})"), ct)
};
static string PublicationFilter(ICollection input) => string.Join(" OR ", input.Select(s => $"message_type = '{s}'"));
}
@@ -129,8 +125,7 @@ private static Task PublicationExists(
this NpgsqlDataSource dataSource,
string publicationName,
CancellationToken ct
- ) =>
- dataSource.Exists("pg_publication", "pubname = $1", [publicationName], ct);
+ ) => dataSource.Exists("pg_publication", "pubname = $1", [publicationName], ct);
public abstract record SetupPublicationResult
{
diff --git a/src/Blumchen/Subscriptions/Management/ReplicationSlotManagement.cs b/src/Blumchen/Subscriptions/Management/ReplicationSlotManagement.cs
index 4595dd4..adeb6a8 100644
--- a/src/Blumchen/Subscriptions/Management/ReplicationSlotManagement.cs
+++ b/src/Blumchen/Subscriptions/Management/ReplicationSlotManagement.cs
@@ -5,11 +5,16 @@
namespace Blumchen.Subscriptions.Management;
using static ReplicationSlotManagement.CreateReplicationSlotResult;
-#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public static class ReplicationSlotManagement
{
#pragma warning disable CA2208
+ private static Task ReplicationSlotExists(
+ this NpgsqlDataSource dataSource,
+ string slotName,
+ CancellationToken ct
+ ) => dataSource.Exists("pg_replication_slots", "slot_name ILIKE $1", [slotName], ct);
+
public static async Task SetupReplicationSlot(
this NpgsqlDataSource dataSource,
LogicalReplicationConnection connection,
@@ -54,18 +59,12 @@ static async Task Create(
}
}
- private static Task ReplicationSlotExists(
- this NpgsqlDataSource dataSource,
- string slotName,
- CancellationToken ct
- ) => dataSource.Exists("pg_replication_slots", "slot_name = $1", [slotName], ct);
-
public record ReplicationSlotSetupOptions(
string SlotName = $"{TableDescriptorBuilder.MessageTable.DefaultName}_slot",
Subscription.CreateStyle CreateStyle = Subscription.CreateStyle.WhenNotExists,
- bool Binary = false //https://www.postgresql.org/docs/current/sql-createsubscription.html#SQL-CREATESUBSCRIPTION-WITH-BINARY
+ bool Binary =
+ false //https://www.postgresql.org/docs/current/sql-createsubscription.html#SQL-CREATESUBSCRIPTION-WITH-BINARY
);
-
public abstract record CreateReplicationSlotResult
{
public record None: CreateReplicationSlotResult;
diff --git a/src/Blumchen/Subscriptions/MimeType.cs b/src/Blumchen/Subscriptions/MimeType.cs
index 8bb3ef9..1197908 100644
--- a/src/Blumchen/Subscriptions/MimeType.cs
+++ b/src/Blumchen/Subscriptions/MimeType.cs
@@ -1,6 +1,5 @@
namespace Blumchen.Subscriptions;
-#pragma warning disable CS1591
public abstract record MimeType(string mimeType)
{
public record Json(): MimeType("application/json");
diff --git a/src/Blumchen/Subscriptions/ObjectTracingConsumer.cs b/src/Blumchen/Subscriptions/ObjectTracingConsumer.cs
new file mode 100644
index 0000000..3263482
--- /dev/null
+++ b/src/Blumchen/Subscriptions/ObjectTracingConsumer.cs
@@ -0,0 +1,11 @@
+namespace Blumchen.Subscriptions;
+
+internal class ObjectTracingConsumer: IHandler