ADR-0044: Postgres replication artefacts are lazy-created, never dropped
ODD Platform creates its notification replication slot and publication on first run if absent, but never drops them — cleanup is the operator's responsibility, by design.
Status
Accepted. Reconstructed from the codebase on 2026-05-30; the decision is live in the source today.
Context
The notification WAL consumer (ADR-0043) needs two Postgres objects to exist: a logical replication slot and a publication. The platform has to ensure they exist before streaming — but it must also decide what to do with them over the platform's lifetime: create them automatically, and if so, ever remove them? Replication slots in particular are durable server-side objects that retain WAL on the primary until consumed, so their lifecycle has real operational weight.
Decision
The platform creates the replication slot and publication lazily on first leader run if they are absent, and never drops them. When the elected consumer starts, it checks for each artefact and creates it only when missing: SELECT EXISTS (... pg_replication_slots WHERE slot_name = ?) then createReplicationSlot() if absent; SELECT EXISTS (... pg_publication WHERE pubname = ?) then CREATE PUBLICATION ... if absent. There is no DROP path anywhere in the notification subsystem — once created, the artefacts persist until an operator removes them.
This makes cleanup the operator's responsibility, by design. The decision keeps boot idempotent (restart, redeploy, or re-elect a leader without disturbing existing artefacts) and avoids the platform ever deleting an object an operator may have created, renamed, or be relying on. It is the same lazy-create-no-drop shape the platform uses for other Postgres-resident artefacts whose lifecycle outlives a single process (for example its range partitions).
Consequences
📌 Bounded growth requires operator action. An unconsumed replication slot makes the Postgres primary retain WAL indefinitely, which can exhaust disk; because the platform never drops the slot, an operator who disables notifications (or renames the slot) must drop the old slot manually. The operator-facing notifications documentation states this cleanup responsibility.
Boot is idempotent: the consumer can start, restart, or fail over without recreating or disturbing existing artefacts — the exists-check makes creation a no-op when they are already present.
The platform never removes an operator's database objects, avoiding the risk of deleting something created or repurposed out-of-band — at the cost of the manual-cleanup burden above.
Changing the configured slot or publication name produces a new artefact on next run and orphans the old one (it is not renamed or dropped) — another instance of the same operator-owns-cleanup consequence.
Evidence
odd-platform-api/.../notification/NotificationSubscriber.java:104-126—registerReplicationSlot(...):SELECT EXISTS (SELECT slot_name FROM pg_replication_slots WHERE slot_name = ?), and only onfalsedoes it callcreateReplicationSlot().logical().withSlotName(...).withOutputPlugin("pgoutput").make().odd-platform-api/.../notification/NotificationSubscriber.java:128-158—registerPublication(...):SELECT EXISTS (SELECT oid FROM pg_publication WHERE pubname = ?), and only onfalsedoes itexecute("CREATE PUBLICATION %s FOR TABLE %s").No
DROPstatement (nopg_drop_replication_slot, noDROP PUBLICATION) exists anywhere in thenotificationpackage — confirmed by sweep — so neither artefact is ever removed by the platform.
See also
ADR-0043 — The notification WAL consumer is a leader-elected singleton — the consumer that creates and relies on these artefacts.
Notifications — the operator-facing guidance, including manual replication-slot cleanup.
Last updated