> **Plain-language companion:** [v0.46.0.md](v0.46.0.md) ## v0.46.0 — Extract `pg_tide`: standalone transactional outbox, inbox, and relay **Status: Released.** Full design in [plans/PLAN_RELAY_STANDALONE.md](../plans/PLAN_RELAY_STANDALONE.md) §7. > **Release Theme** > Extract the outbox, inbox, and relay subsystems from `pg_trickle` into a > new standalone extension `pg_tide` (`trickle-labs/pg-tide`). `pg_trickle` > is left shipping exactly one thing: incremental view maintenance. All event > messaging surface moves to a product that is useful independently. --- ### Motivation The outbox/inbox/relay code (~6,150 Rust LOC, ~2,500 SQL LOC) has lived inside `pg_trickle` since v0.28–v0.29. It works, but it is an architectural mismatch: - Different audience (microservices engineers vs. IVM users) - Different release cadence (relay backends evolve independently of DVM) - Different deployment story (`pg_tide` + binary; `pg_trickle` extension only) - ~6 optional async messaging crates + AWS SDK in the extension's `Cargo.lock` The feature set has no known production users at the time of the cut, making this a clean break with no backwards-compatibility obligation. See [plans/PLAN_RELAY_STANDALONE.md](../plans/PLAN_RELAY_STANDALONE.md) §7.8 for the full rationale. --- ### What ships in the new `pg_tide` repo **Extension (schema `tide`):** | API | Description | |-----|-------------| | `tide.outbox_create(name, ...)` | Create a named outbox table with retention config and claim-check threshold | | `tide.outbox_publish(name, payload, headers)` | Write a row transactionally from inside any application transaction | | `tide.outbox_status(name)` | View lag, consumer offsets, and claim-check pressure | | `tide.outbox_disable(name)` | Pause publishing without dropping the table | | `tide.inbox_create(name, ...)` | Create an idempotent inbox with optional DLQ, priority, ordering, retention | | `tide.inbox_mark_processed(name, event_id)` | Acknowledge successful processing | | `tide.inbox_mark_failed(name, event_id, error)` | Send to DLQ after max retries | | `tide.replay_inbox_messages(name, ...)` | Re-queue events from DLQ | | `tide.relay_set_outbox(name, config)` | Configure an outbox pipeline | | `tide.relay_set_inbox(name, config)` | Configure an inbox pipeline | | `tide.relay_enable/disable/delete(name)` | Lifecycle management | | `tide.relay_list_configs()` | Inspect active pipelines | | `tide._pending` / `_dlq` / `_stats` | Plain VIEWs — no IVM overhead | **Binary (`pg-tide`):** - Lifted verbatim from `pgtrickle-relay/` using `git filter-repo` to preserve history. - Six source backends: NATS, Kafka, outbox poller, webhook, Redis, SQS, RabbitMQ. - Eight sink backends: same set + stdout + pg-inbox. - Advisory-lock leader election, independent metrics/health server (Axum + Prometheus). **No `pg_trickle` required:** `pg_tide.control` has no `requires`. The inbox helper views are plain VIEWs; the one integration point is `attach_outbox()` on the `pg_trickle` side (see below). --- ### Changes in `pg_trickle` (this repo) | Item | Change | |------|--------| | `src/api/outbox.rs` (1,048 LOC) | Deleted | | `src/api/inbox.rs` (1,215 LOC) | Deleted | | Relay catalog SQL in `src/lib.rs` (~240 LOC) | Deleted | | `pgtrickle-relay/` crate (~3,650 LOC) | Moved to `trickle-labs/pg-tide` | | `Dockerfile.relay` | Deleted | | 4 relay publish jobs in `release.yml` | Deleted | | `aws-smithy-http-client` exception in `security.yml` | Deleted | | `pgtrickle-relay` in workspace `Cargo.toml` | Removed | | `pgtrickle.enable_outbox()` / `pgtrickle.create_inbox()` | Replaced by `pgtrickle.attach_outbox()` | | New `pgtrickle.attach_outbox(stream_table, ...)` | Calls `tide.outbox_create()` and registers a refresh-time SPI hook to `tide.outbox_publish()` — same transaction, ADR-001/ADR-002 atomicity preserved | **Upgrade script** (`pg_trickle--0.45.0--0.46.0.sql`) drops all old `pgtrickle.relay_*` objects and all old outbox/inbox API functions. Base tables (`pgtrickle.outbox_`, `pgtrickle.inbox_<...>`) are left in place for manual data migration; the CHANGELOG documents the explicit equivalents in `pg_tide`. --- ### Integration point: `attach_outbox()` and atomicity The critical invariant from ADR-001/ADR-002: the outbox row and the refresh MERGE must commit in the same transaction. After the cut: 1. `pgtrickle.attach_outbox('orders_stream', ...)` calls `tide.outbox_create('outbox_orders_stream', ...)` once at setup time. 2. Inside each refresh, the hot path calls `SELECT tide.outbox_publish($1, $2, $3)` via SPI. The SPI call runs in the current transaction — same atomicity guarantee as a direct INSERT. 3. `pg_trickle.control` does **not** declare `requires = 'pg_tide'`. At `attach_outbox()` call time, the function probes `to_regproc('tide.outbox_create(...)')` and raises with an actionable install hint if `pg_tide` is absent. All other IVM features (refresh, CDC, scheduler, DAG) are unaffected. --- ### Work plan (6 working days) See [plans/PLAN_RELAY_STANDALONE.md §7.9](../plans/PLAN_RELAY_STANDALONE.md) for the day-by-day breakdown. **Days 1–2:** Bootstrap `trickle-labs/pg-tide`; lift outbox/inbox into the extension; carry over tests. **Day 3:** Lift relay catalog SQL and `pgtrickle-relay/` binary into the new repo. **Day 4:** Delete moved code from this repo; add `attach_outbox()`; write the upgrade script. **Day 5a:** Documentation — move and rewrite the four outbox/inbox/relay docs; update cross-references in ~12 docs in this repo. **Day 5b:** CI / packaging — clone CI templates into `pg-tide`; verify both repos build clean; cut pre-release tags. **Pre-cut action (Day 3 gate):** Post a one-line announcement to the `pg_trickle` discussions board confirming no known users of `enable_outbox()` / `create_inbox()` / the relay binary. --- ### Success criteria - `cargo build -p pg_trickle` pulls no relay-related dependency. - A user installing only `pg_tide` (without `pg_trickle`) can configure a NATS-to-webhook pipeline and run it end-to-end against vanilla Postgres 18. - `pgtrickle.relay_*` tables and functions no longer exist in a fresh install. - `pgtrickle.attach_outbox()` integration test passes with `pg_tide` installed and raises a clear error without it. - Total LOC removed from this repo: ≥6,150 Rust + ≥2,500 SQL. --- ### Assets moving to `trickle-labs/pg-tide` See [plans/PLAN_RELAY_STANDALONE.md §7.10](../plans/PLAN_RELAY_STANDALONE.md) for the full inventory: - `docs/OUTBOX.md`, `docs/INBOX.md`, `docs/RELAY.md`, `docs/RELAY_GUIDE.md` - `tests/e2e_outbox_tests.rs`, `tests/e2e_inbox_tests.rs` - `plans/relay/PLAN_RELAY_WIRE_FORMATS.md`, `PLAN_RELAY_CLI.md`, `PLAN_RELAY_CLI_PHASE_2.md`, `PLAN_RELAY_GAPS_FROM_FELDERA_RISINGWAVE.md` - Blog posts: `built-in-outbox.md`, `outbox-pattern-turbocharged.md`, `inbox-pattern-kafka.md`, `relay-deep-dive.md` - CI templates to clone (not move): pgrx composite action, skill files, AGENTS.md, lint/test/coverage/release workflows, justfile, deny.toml, stability-test workflow --- *Previous: [v0.45.0 — Operational Readiness, Scalability & CI Completeness](v0.45.0.md)* *Next: [v0.47.0 — Embedding Pipeline Infrastructure & ANN Maintenance](v0.47.0.md)*