Idle Subscription Cleanup for NATS, RabbitMQ, SQL Server and Postgres
The NATS, RabbitMQ, SQL Server and Postgres transports are all now at 1.1.0. The headline change is the same one Redis got in 4.4.0: orphaned event subscriptions are cleaned up automatically.
What changed
Every multicast event subscriber gets its own per-instance subscription, named from the instance name you pass to WithNames. On Kubernetes, ECS and similar platforms that name changes on every deploy, so each one leaves a subscription behind that nothing will ever consume, and messages keep being fanned out to it.
1.1.0 fixes this on each transport using whatever the backend does best:
- RabbitMQ: subscription queues are declared with
x-expiresset to your idle window, so the broker deletes a queue once it has had no consumers for that long. Works the same on LavinMQ. - NATS (JetStream): durable consumers get an
InactiveThresholdmatching the window, topic streams discard messages older than the window, and a periodic sweep removes retry streams that have had no consumers for two consecutive sweeps. Core NATS needs no cleanup. - SQL Server and Postgres: each subscription heartbeats a new
LastSeenAtcolumn while its instance runs. A periodic sweep deletes subscriptions with a lapsed heartbeat along with their queued messages, and publishers skip them. Timing uses database time, so clock skew between hosts doesn’t matter.
The AMQP transport is unchanged. Durable subscriptions there are owned by the broker, so the AMQP docs now explain how to configure cleanup on the broker side instead.
Configuring the idle window
The window is the same on all four transports. It defaults to 4 days, with a 5 minute minimum:
var bus = new BusBuilder()
.Configure()
.WithNames("OrderService", Environment.MachineName)
.WithTransport(new RabbitMqTransportConfiguration().WithHost("localhost"))
.WithAutoDeleteOnIdle(TimeSpan.FromDays(1)) // Optional, defaults to 4 days, minimum 5 minutes
.WithTypesFrom(typeProvider)
.WithAutofacDefaults(container)
.Build();
A running instance never expires, so the window only matters once an instance stops. If an instance stays down longer than the window, events published in the meantime are not delivered when it returns. Raise WithAutoDeleteOnIdle if your instances routinely go quiet for long periods.
Upgrading
dotnet add package Nimbus.Transports.Nats --version 1.1.0
dotnet add package Nimbus.Transports.RabbitMQ --version 1.1.0
dotnet add package Nimbus.Transports.SqlServer --version 1.1.0
dotnet add package Nimbus.Transports.Postgres --version 1.1.0
No breaking changes, and the idle window is optional. Two things to know:
- SQL Server and Postgres: existing databases need a
LastSeenAtcolumn on the subscriptions table.WithAutoCreateSchema()adds it for you. Otherwise see the SQL Server and Postgres docs for theALTER TABLEstatement. - RabbitMQ: RabbitMQ won’t redeclare an existing queue with different arguments, so queues created by 1.0.0 keep working but won’t expire. Delete them once and they are recreated with
x-expireson the next start, or apply anexpirespolicy yourself. Nimbus logs a warning for each one it finds.
Full details are in the NATS, RabbitMQ, SQL Server and Postgres transport docs.