pg_durable
Overview
| Package | Version | Category | License | Language |
|---|---|---|---|---|
pg_durable | 0.2.2 | FEAT | PostgreSQL | Rust |
| ID | Extension | Bin | Lib | Load | Create | Trust | Reloc | Schema |
|---|---|---|---|---|---|---|---|---|
| 2870 | pg_durable | No | Yes | Yes | Yes | No | No | df |
Requires shared_preload_libraries=pg_durable and a superuser worker role. Upstream README targets PG17; DEB validated PG14-18 on u24a arm64, RPM spec targets PG14-18; pgrx patched to 0.18.1.
Version
| Type | Repo | Version | PG Ver | Package | Deps |
|---|---|---|---|---|---|
| EXT | PIGSTY | 0.2.2 | 1817161514 | pg_durable | - |
| RPM | PIGSTY | 0.2.2 | 1817161514 | pg_durable_$v | - |
| DEB | PIGSTY | 0.2.2 | 1817161514 | postgresql-$v-pg-durable | - |
Build
You can build the RPM / DEB packages for pg_durable using pig build:
pig build pkg pg_durable # build RPM / DEB packages
Install
You can install pg_durable directly. First, make sure the PGDG and PIGSTY repositories are added and enabled:
pig repo add pgsql -u # Add repo and update cache
Install the extension using pig or apt/yum/dnf:
pig install pg_durable; # Install for current active PG version
pig ext install -y pg_durable -v 18 # PG 18
pig ext install -y pg_durable -v 17 # PG 17
pig ext install -y pg_durable -v 16 # PG 16
pig ext install -y pg_durable -v 15 # PG 15
pig ext install -y pg_durable -v 14 # PG 14
dnf install -y pg_durable_18 # PG 18
dnf install -y pg_durable_17 # PG 17
dnf install -y pg_durable_16 # PG 16
dnf install -y pg_durable_15 # PG 15
dnf install -y pg_durable_14 # PG 14
apt install -y postgresql-18-pg-durable # PG 18
apt install -y postgresql-17-pg-durable # PG 17
apt install -y postgresql-16-pg-durable # PG 16
apt install -y postgresql-15-pg-durable # PG 15
apt install -y postgresql-14-pg-durable # PG 14
Preload:
shared_preload_libraries = 'pg_durable';
Create Extension:
CREATE EXTENSION pg_durable;
Usage
Sources:
pg_durable runs durable, fault-tolerant SQL workflows inside PostgreSQL. A workflow is a graph of SQL strings, functions, timers, signals, conditions, and parallel branches submitted with df.start(). The extension checkpoints state in PostgreSQL so completed steps are not re-executed after crashes, restarts, or failed steps.
The current main branch reports package version 0.2.4; the latest tagged release observed for this refresh is v0.2.3. Upstream published packages and Docker images focus on PostgreSQL 17 and 18, while local Pigsty metadata may validate a broader PostgreSQL range separately.
Enable and Grant Access
CREATE EXTENSION pg_durable;
SELECT df.grant_usage('app_role');
pg_durable must be loaded through shared_preload_libraries, followed by a PostgreSQL restart. The background worker initializes asynchronously after CREATE EXTENSION; retry if df.* calls report that the worker is not initialized yet. CREATE EXTENSION does not grant usage to PUBLIC, so run df.grant_usage() for application roles and repeat it after extension upgrades so new functions are covered.
The worker connects to pg_durable.database and runs as pg_durable.worker_role. The worker role must be a superuser because it bypasses RLS to manage all users’ durable instances.
Start and Monitor Workflows
SELECT df.start('SELECT ''Hello, durable world!''', 'hello-job');
SELECT *
FROM df.list_instances();
SELECT df.status('a1b2c3d4');
SELECT df.result('a1b2c3d4');
SELECT df.cancel('a1b2c3d4', 'No longer needed');
df.start() returns an instance ID. Status, result, cancel, signal, explain, and await helpers operate on the instance ID, not on the label. df.list_instances() has a basic overload and a paginated overload; pass at least three arguments, using NULL for skipped filters, when you need created_at, completed_at, and next_cursor.
Compose SQL Steps
-- Run one step, name its result, then substitute it in the next step.
SELECT df.start(
'SELECT 100 AS amount' |=> 'total'
~> 'SELECT $total * 2 AS doubled',
'double-total'
);
-- Pass a multi-row result set between steps.
SELECT df.start(
'SELECT id FROM documents WHERE processed = false LIMIT 100' |=> 'batch'
~> 'UPDATE documents SET processed = true WHERE id IN (SELECT id FROM $batch.*)',
'process-documents'
);
-- Branch on a SQL condition.
SELECT df.start(
'SELECT count(*) > 10 FROM orders'
?> 'SELECT ''high volume'''
!> 'SELECT ''low volume''',
'order-volume'
);
Core operators are ~> for sequence, |=> for naming a result, & for join, | for race, ?> and !> for conditional branches, and @> for loops. Result substitution supports $name, $name.column, null-safe $name? / $name.column?, and row-set expansion through $name.*.
Timers, Schedules, Signals, and Variables
SELECT df.start(
@> (
df.wait_for_schedule('0 * * * *')
~> 'SELECT run_hourly_rollup()'
),
'hourly-rollup'
);
SELECT df.start(
'SELECT create_invoice()' |=> 'invoice'
~> df.wait_for_signal('approval', 86400)
~> 'SELECT finalize_invoice($invoice.id)',
'invoice-approval'
);
SELECT df.setvar('api_url', 'https://example.internal');
SELECT df.getvar('api_url');
Useful DSL functions include df.sleep(seconds), df.wait_for_schedule(cron), df.wait_for_signal(name, timeout), df.signal(id, name, data), df.join(), df.join3(), df.race(), df.if(), df.if_rows(), df.loop(), df.break(), df.await_instance(), df.explain(), and durable variable helpers.
Monitoring and Runtime State
SELECT * FROM df.instance_nodes('a1b2c3d4');
SELECT * FROM df.instance_executions('a1b2c3d4', 20);
-- Requires a direct admin grant; df.grant_usage() does not include it.
SELECT * FROM df.metrics();
df.instance_nodes() reports both stored node status and inferred status. The inferred view adds skipped for untaken branches or race losers and reinterprets superseded loop-iteration nodes as pending when a newer iteration has taken over. This is a read-time interpretation and does not change workflow execution semantics.
Configuration and Caveats
- Required preload: add
pg_durabletoshared_preload_librariesand restart PostgreSQL. pg_durable.databasemust name the database where the extension is created; workflows are not processed in a different database unless you explicitly use the supported database argument where available.- Fresh installs after the v0.2.3 provider-schema move use
_duroxide; installs upgraded from <= 0.2.2 keep the legacyduroxideschema.df.duroxide_schema()reports which schema an install should use. pg_durable.worker_rolemust exist and be a superuser.- Connection-related GUCs include
pg_durable.max_management_connections,pg_durable.max_duroxide_connections,pg_durable.max_user_connections, andpg_durable.execution_acquire_timeout. df.http()performs outbound HTTP work. Standard grants exclude HTTP unlessdf.grant_usage(..., include_http => true)is used, and release builds may restrict HTTP egress by feature.- Upstream status is preview. The published Docker image is intended for evaluation and learning, not production.
Feedback
Was this page helpful?
Thanks for the feedback! Please let us know how we can improve.
Sorry to hear that. Please let us know how we can improve.