Outboxhard5-8 years

Your outbox table's relay is a polling job that runs `SELECT ... FOR UPDATE SKIP LOCKED` every 200ms. Explain exactly why SKIP LOCKED is what makes running several relay instances safe, and then explain what changes — mechanically — if you switch that relay to Debezium reading PostgreSQL's write-ahead log instead.

Without SKIP LOCKED, two relay instances querying the same unpublished rows would either both grab the same row (double-publishing worse than at-least-once already allows) or one would block waiting for the other's lock, serializing what was supposed to be parallel work. FOR UPDATE SKIP LOCKED makes each worker's query silently skip any row another worker already has locked rather than blocking on it or double-claiming it — that's what makes multiple relay instances safe to run concurrently; it's about correctness under concurrency, not raw speed, since a single worker's throughput is bounded by the poll interval and one round trip per cycle either way. CDC changes the mechanism entirely rather than making the same query faster: Debezium never runs a SELECT against the outbox table at all (past a one-time initial snapshot) — it opens a logical replication connection and reads the stream of already-decoded, already-committed row changes straight off PostgreSQL's write-ahead log, which is sub-second and puts no load on the table, at the cost of running a Kafka Connect deployment and a replication slot that has to be monitored, because a stalled slot holds back WAL for the whole database instance, not just the outbox table.

The lesson behind it →
More on Outbox