pgtrickle-relay

Standalone Rust CLI binary that bridges pg_trickle outbox and inbox tables with external messaging systems.

Overview

pgtrickle-relay runs as a sidecar or standalone process alongside your PostgreSQL instance. It supports two directions:

  • Forward mode: Polls pg-trickle outbox tables and publishes deltas to external sinks.
  • Reverse mode: Consumes messages from external sources and writes them into pg-trickle inbox tables.

Both directions share the same source/sink trait abstractions, config system, observability stack, and error handling.

Supported Backends

Backend Feature flag Forward (sink) Reverse (source)
NATS JetStream nats (default)
HTTP webhook webhook (default)
stdout / file stdout (default)
Apache Kafka kafka
Redis Streams redis
AWS SQS sqs
RabbitMQ rabbitmq
PostgreSQL inbox pg-inbox

Installation

From source

cargo install --path pgtrickle-relay --features default

From Docker

docker pull ghcr.io/grove/pgtrickle-relay:0.29.0

Configuration

All relay pipelines are configured via SQL — no YAML files required.

Register a forward pipeline (outbox → NATS)

SELECT pgtrickle.set_relay_outbox(
    'orders-to-nats',
    config => '{
        "stream_table": "orders_stream",
        "sink_type": "nats",
        "nats_url": "nats://localhost:4222",
        "nats_stream": "pgtrickle",
        "subject_template": "pgtrickle.{stream_table}.{op}"
    }'::jsonb
);
SELECT pgtrickle.enable_relay('orders-to-nats');

Register a reverse pipeline (NATS → inbox)

SELECT pgtrickle.set_relay_inbox(
    'nats-to-orders-inbox',
    config => '{
        "source_type": "nats",
        "nats_url": "nats://localhost:4222",
        "nats_stream": "commands",
        "nats_consumer": "relay-consumer",
        "inbox_table": "orders_inbox"
    }'::jsonb
);
SELECT pgtrickle.enable_relay('nats-to-orders-inbox');

Manage pipelines

-- List all pipelines
SELECT * FROM pgtrickle.list_relay_configs();

-- Disable a pipeline
SELECT pgtrickle.disable_relay('orders-to-nats');

-- Delete a pipeline
SELECT pgtrickle.delete_relay('orders-to-nats');

Running the relay

pgtrickle-relay \
  --postgres-url "postgres://relay:password@localhost:5432/mydb" \
  --metrics-addr "0.0.0.0:9090" \
  --log-format json \
  --relay-group-id "dc1-relay"

Environment variables

Variable CLI flag Default
PGTRICKLE_RELAY_POSTGRES_URL --postgres-url (required)
PGTRICKLE_RELAY_METRICS_ADDR --metrics-addr 0.0.0.0:9090
PGTRICKLE_RELAY_LOG_FORMAT --log-format text
PGTRICKLE_RELAY_LOG_LEVEL --log-level info
PGTRICKLE_RELAY_GROUP_ID --relay-group-id default

Observability

The relay exposes: - GET /metrics — Prometheus metrics (messages published, consumed, errors, lag) - GET /health — JSON health check

curl http://localhost:9090/health
# {"status":"healthy","pipelines_owned":3,"uptime_seconds":42}

High Availability

Multiple relay instances can run against the same PostgreSQL database. Pipeline ownership is distributed via PostgreSQL advisory locks — each pipeline is owned by exactly one relay instance at a time. If an instance dies, another acquires the lock on the next discovery interval.

No external coordinator (ZooKeeper, etcd, etc.) is required.

Hot Reload

Config changes take effect without restart. The relay listens on the pgtrickle_relay_config PostgreSQL notification channel. When you run set_relay_outbox, enable_relay, disable_relay, or delete_relay, the relay reloads the affected pipeline within seconds.

Building with specific backends only

# Kafka only (no NATS, no webhook)
cargo build --no-default-features --features kafka,stdout

# All backends
cargo build --features nats,webhook,kafka,redis,sqs,rabbitmq,pg-inbox,stdout

Subject / topic templates

Subject templates support these variables:

Variable Description
{stream_table} Name of the source stream table
{op} Operation: insert, update, delete
{outbox_id} Sequential outbox batch ID
{refresh_id} pg-trickle refresh ID

Example: pgtrickle.{stream_table}.{op}pgtrickle.orders.insert

License

Apache-2.0. See LICENSE.