prolly-store-postgres
PostgreSQL-backed remote store adapter for prolly-map.
This crate implements RemoteStoreBackend using sqlx::PgPool. Use it through
RemoteProllyStore and AsyncProlly when you want Prolly tree nodes, traversal
hints, and named root manifests in PostgreSQL.
Installation
[]
= "0.5.1"
= "0.4.0"
= { = "1", = ["macros", "rt-multi-thread"] }
When to use it
Use this adapter when your application already depends on PostgreSQL and you want durable, transactional storage for versioned maps. It is a good fit for server-side systems that need normal SQL backup/restore operations, managed Postgres reliability, and atomic named-root updates.
Prefer this adapter over Redis when named roots are durable business state. Use object-store or NoSQL adapters when the node volume is very large or when your deployment is already centered on those services.
Data model
initialize_schema creates three tables:
prolly_nodes(cid BYTEA PRIMARY KEY, node BYTEA NOT NULL)prolly_hints(namespace BYTEA, key BYTEA, value BYTEA)prolly_roots(name BYTEA PRIMARY KEY, manifest BYTEA NOT NULL)
Nodes are content addressed by CID. Root manifests give stable names to immutable
tree handles, such as main, tenant/42/head, or sync/job/abc/checkpoint.
Setup
Run PostgreSQL locally:
Or use the Prolly service compose file from the Prolly repo root:
Set the connection URL:
The adapter can create its own tables:
async
For large publications or memory-constrained services, configure the maximum number of node or root items sent in one set-based SQL statement:
use NonZeroUsize;
use ;
async
PostgresBackend::new and PostgresBackend::connect use 1,024-item batches.
You can also pass options to a caller-owned pool with
PostgresBackend::new_with_options.
Basic usage
use ;
use PostgresBackend;
async
Diff, merge, and conflict resolution
Each update returns a new immutable Tree. Old and new trees share unchanged
subtrees, so diffs and merges only need to inspect changed branches:
use ;
use PostgresBackend;
async
Operational notes
initialize_schemais idempotent and safe to run during startup.- Strict commits validate named-root preconditions and apply node and root writes in one PostgreSQL transaction.
- Node batch reads preserve requested order, duplicates, and missing values
with chunked
UNNESTqueries. Node publications use chunked set-based upserts. - Root writers use transaction-scoped advisory locks derived with
hashtextextended('prolly-root-v1:' || encode(name, 'hex'), 0). Multi-root commits sort and deduplicate names before acquiring locks, so unrelated roots can progress concurrently without lock-order deadlocks. Hash collisions can serialize unrelated roots but cannot change results. - Node rows are content-addressed and can be shared by many named roots.
- Removing a named root does not immediately delete unreachable nodes. Use a higher-level retention/GC flow before pruning nodes.
- Keep PostgreSQL connection pooling aligned with your app concurrency. The
adapter uses the
PgPoolyou provide.
Running the example
From the standalone repository root:
The example initializes schema, writes a base tree, computes diffs, merges branches, resolves a conflict, publishes a named root, and loads it back.
Testing
The integration test runs when PROLLY_STORE_POSTGRES_URL is set and returns
without connecting otherwise:
Run it against a disposable database or schema. The adapter tables are shared by every client using that database.
See the prolly-map API documentation for the
async map, transaction, diff, and merge APIs used with this backend.