.NET library · PostgreSQL logical replication
Every committed row change, as JSON.
PgOutput2Json reads PostgreSQL's replication stream and hands each insert, update and delete to your .NET code, or publishes it to Kafka, RabbitMQ, Redis and other outputs, shortly after the transaction commits.
dotnet add package PgOutput2Json
PostgreSQL, committed
INSERT INTO orders (id, status, total)
VALUES (42, 'new', 19.90);Your handler, or a broker
{
"c": "I",change type
"w": 2485645760,transaction final LSN
"n": 1,message number
"r": { "id": 42, "status": "new", "total": 19.90 }
}PostgreSQL, committed
UPDATE orders SET status = 'paid'
WHERE id = 42;Your handler, or a broker
{
"c": "U",change type
"w": 2485645912,transaction final LSN
"n": 1,message number
"r": { "id": 42, "status": "paid", "total": 19.90 }
}PostgreSQL, committed
DELETE FROM orders
WHERE id = 42;Your handler, or a broker
{
"c": "D",change type
"w": 2485646048,transaction final LSN
"n": 1,message number
"k": { "id": 42 }
}Outputs
One stream in, many places to send it
Handle the messages in your own code, or add a package and let it publish them. Each output has its own page in the docs.
- Your .NET codeA delegate per change
PgOutput2Json - KafkaTopics, idempotent producer
PgOutput2Json.Kafka - RabbitMQTopic exchange
PgOutput2Json.RabbitMq - RabbitMQ StreamsStreams and super streams
PgOutput2Json.RabbitMqStreams - RedisStreams or Pub/Sub
PgOutput2Json.Redis - SQLiteTables kept in sync
PgOutput2Json.Sqlite - MongoDBCollections kept in sync
PgOutput2Json.MongoDb - Amazon KinesisData streams
PgOutput2Json.Kinesis - Amazon DynamoDBTables kept in sync
PgOutput2Json.DynamoDb - ClickHouseTables kept in sync
PgOutput2Json.ClickHouse - Azure Event HubsPartitioned events
PgOutput2Json.AzureEventHubs - WebhooksBatched HTTP requests
PgOutput2Json.Webhooks - Something elseWrite your own output
IMessagePublisherFactory
How it behaves
Built for the cases where a change must not be lost
Nothing to install in PostgreSQL
It reads pgoutput, the logical decoding plugin that ships with PostgreSQL 10 and later. You need wal_level = logical, a publication and a replication user.
Getting startedAt-least-once, with a key to deduplicate
A slot replays unconfirmed changes after a crash or a reconnect, and they are published again. Every message carries a key, the transaction LSN and a counter, so consumers can skip what they already have.
Delivery and deduplicationConfirmed only after the output has it
Changes are published in batches. A batch is confirmed to PostgreSQL after the output acknowledged it, so a failure replays the batch.
ConfigurationChanges survive a restart
With a permanent replication slot, PostgreSQL keeps the changes made while your worker is stopped and sends them when it connects again.
Replication slotsStable routing for partitioned outputs
Choose the columns to partition by. Inserts, updates and deletes of a row go to the same Kafka partition, super stream partition or Event Hubs partition.
Message keyCompact mode
Optional. Rows as arrays of values, with the column names sent once per table, for smaller messages. By default a row is an object with its column names.
Change events
Quick start
A publication, a package, a handler
Set wal_level = logical in postgresql.conf, then:
- Create a replication user and a publication for the tables you want.
- Add the
PgOutput2Jsonpackage to a .NET worker service. - Build the listener with a handler, or with one of the outputs, and start it.
CREATE USER pgoutput2json WITH
PASSWORD '_your_password_here_'
REPLICATION;
CREATE PUBLICATION my_publication
FOR TABLE my_table1, my_table2
WITH (publish = 'insert, update, delete');using var pgOutput2Json = PgOutput2JsonBuilder.Create()
.WithLoggerFactory(_loggerFactory)
.WithPgConnectionString("server=localhost;database=my_database;username=pgoutput2json;password=_your_password_here_")
.WithPgPublications("my_publication")
.WithMessageHandler((json, table, key, partition) =>
{
Console.WriteLine($"{table}: {json}");
return Task.FromResult(true);
})
.Build();
await pgOutput2Json.StartAsync(stoppingToken);Use it for
Reacting to data, not polling for it
- Background tasks triggered by inserts and updates
- Application caches updated with low latency
- Asynchronous logging to a file or another database
- Real-time aggregation
- Lightweight ETL into a data warehouse
Start with one table
Every output is tested on each change, against PostgreSQL 14 to 18. The library targets .NET 8, 9 and 10.