# v0.28.0 — Transactional Inbox & Outbox Patterns

> **Full technical details:** [v0.28.0.md-full.md](v0.28.0.md-full.md)

**Status: ✅ Released** | **Scope: Large** (~6 weeks)

> Reliable event messaging built entirely inside PostgreSQL — no Kafka,
> no Debezium, no external infrastructure required.

---

## What problem does this solve?

Modern applications often need to communicate between services: "an order was
placed, notify the warehouse system." The naive approach — write to the
database, *then* publish the event to a message broker — has a fatal flaw:
if the application crashes between those two steps, either the database row
exists with no event published, or the event was published but the database
write failed. This is called the *dual-write problem*, and it causes data
inconsistencies that are extremely difficult to debug.

v0.28.0 solves this entirely inside PostgreSQL, with no external
infrastructure required.

---

## The Transactional Outbox

Think of the **outbox** as a guaranteed delivery queue built directly into
your database. When a stream table is refreshed and its result changes,
pg_trickle automatically writes a record of those changes into a companion
table — the outbox — *in the same database transaction as the refresh*.
Either both succeed, or neither does. There is no window where the data
changed but the event was not recorded.

An external relay process (your code, or the built-in one from v0.29.0)
then reads the outbox and publishes to whatever downstream system you use:
Kafka, NATS, a webhook, an SQS queue, or anything else. The relay can
crash and restart safely — it just picks up where it left off.

For large batches of changes (more than 10,000 rows by default), pg_trickle
uses a *claim-check* pattern: the outbox row carries only a lightweight
summary, and the full row data is stored in a companion table that the relay
reads in bounded memory chunks. This means delivery is never blocked by the
size of a delta.

---

## The Transactional Inbox

The **inbox** is the mirror image: a production-grade table for *receiving*
events from outside. Call `create_inbox('my_inbox')` and pg_trickle
automatically creates:

- A **pending messages** stream table showing all unprocessed events
- A **dead-letter queue** stream table for messages that have failed too
  many times
- A **statistics** stream table tracking processing throughput and error
  rates

Applications insert events into the inbox with `ON CONFLICT DO NOTHING`
for automatic deduplication — the same event published twice only creates
one row. If a message processor crashes mid-flight, the message stays
pending and will be picked up again.

---

## Consumer Groups (Kafka-style, built into PostgreSQL)

For high-throughput scenarios where multiple relay processes share a single
outbox, **consumer groups** let them coordinate safely — exactly like Kafka
consumer groups, but with zero extra infrastructure. Each relay claims a
batch under a *visibility timeout* (similar to Amazon SQS), and if the relay
crashes its batch automatically becomes available for another relay to claim
after the timeout expires.

Live dashboards of consumer health — lag, last heartbeat, active leases —
are maintained as stream tables that can feed directly into Grafana.

---

## Ordered Message Processing

For use cases where the order of messages matters — financial transactions,
audit trails, order management — `enable_inbox_ordering()` creates a
`next_<inbox>` stream table that surfaces *only the next expected message*
for each entity (customer, order, account). Out-of-order arrivals are
withheld until the preceding message has been processed. A separate gap
detection stream table automatically alerts when a message appears to be
permanently missing.

Priority queues let critical messages use a one-second refresh schedule
while background messages use thirty seconds, with no interference between
tiers.

---

## Scope

v0.28.0 is a substantial release: six weeks of solo engineering effort
covering the full outbox/inbox stack, consumer groups, ordered processing,
benchmarks, and documentation. The result is a self-contained, reliable
event-driven messaging system that needs nothing outside PostgreSQL.

---

*Next: [v0.29.0 — Relay CLI](v0.29.0.md)*
