diff --git a/README.md b/README.md index e71eb38a05..d218a66e33 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@

PGX

- PGX. Postgres for agents. 🐘
+ Every AI agent gets its own Postgres.
One runtime. Thousands of Postgres databases.

@@ -12,6 +12,21 @@ License: AGPL-3.0

+

+ Quickstart · + Agents · + Why PGX · + Benchmarks · + Architecture · + Status · + Docs +

+ +

+ Install one Skill. Your coding agent handles the database.
+ No Docker required · Native Mac & Linux · Automatic cleanup +

+ ```text # Claude Code: install inside a session (native plugin, lightweight repository) /plugin marketplace add pgrundev/pgx-plugin @@ -26,11 +41,6 @@ npx skills add pgrundev/pgx --skill pgx npx pgxdb@latest ``` -

- Every AI agent gets its own Postgres.
- Install one Skill. Your coding agent handles the database.
- No Docker required · Native Mac & Linux · Automatic cleanup -

@@ -56,55 +66,24 @@ PGX is the database engine behind PGRun branches. ## Why PGX +

+ PGX architecture: your machine's agents, CI and apps speak the Postgres wire protocol to one PGX process, a shared Rust SQL engine with a thread per connection, shared catalog and caches, and one isolated database per client, each branched copy-on-write from a sealed template +

+ Agent workflows, test suites, CI jobs and preview environments create databases constantly and throw them away minutes or hours later. Giving each one its own PostgreSQL server costs a process, tens of megabytes and -a cold start every time. Traditional PostgreSQL is built to keep one -database running for years; PGX is built to create one in milliseconds, -use it briefly and delete it. - -Instead of +a cold start every time. PostgreSQL is built to keep one database running +for years; PGX is built to create one in milliseconds, use it briefly and +delete it. ```text -1 database → 1 PostgreSQL server -``` - -PGX runs - -```text -1 PGX runtime → hundreds or thousands of isolated databases +1 PostgreSQL server → 1 database +1 PGX runtime → hundreds or thousands of isolated databases ``` Immutable state is shared. Each database pays mainly for what makes it -different. - -## At a glance - -```text -~6.2 ms warm database mint, p50 -~70 KB memory per untouched idle database -~128 KB memory per idle database that has been queried -1,000 isolated databases on a 4 vCPU / 8 GB VM -128–155 MB total runtime memory with 1,000 untouched databases -~213 MB after all 1,000 have been used - -10 GB template -~674 ms copy-on-write branch ready -~13.6 MB initial additional physical storage - -219 / 231 PostgreSQL src/test/regress byte-exact against PG 18.6 expected output -0 result differences (the other 12 differ in EXPLAIN plan text only) -``` - -These describe measured configurations and workloads, not a claim that -PGX is faster than PostgreSQL in general. The regression figure is -PostgreSQL's own test suite on one platform under the regress profile -(see Benchmarks), not a compatibility percentage for arbitrary SQL. The detail and the scripts are -under Benchmarks. - -## Disposable Postgres - -PGX is built around a different database lifecycle: +different, and follows a lifecycle built for being thrown away: ```text sealed template @@ -116,184 +95,196 @@ agent / CI / test minutes or hours discard dropped a grace period after the last session ``` -A database is created from a sealed template without copying it. On -Linux, ZFS block cloning lets a branch share every unchanged block with +On Linux, ZFS block cloning lets a branch share every unchanged block with its template, so a branch of a 10 GB database is ready in under a second -and starts at about 14 MB of its own storage. - -## Built for agents +and starts at about 14 MB of its own storage. Every agent, every pull +request and every test run can have its own real Postgres-compatible +database. -A coding agent's database workload looks like this, and many agents do it -at once: +## Disposable by design -```text -create database → run migrations → change schema → load fixtures -→ run tests → inspect results → throw the database away -``` +Postgres was built to run for years. An AI agent needs a database for two +minutes: create → migrate → test → destroy. Multiply that by 1,000 agents +running in parallel and you do not need 1,000 Postgres servers. You need +one runtime holding thousands of isolated, disposable databases, each +dropped the moment its agent, pull request or CI job is done. -PGX optimizes for that lifecycle rather than for a long-running -production primary. Every agent, every pull request and every test run -can have its own real Postgres-compatible database. +

+ Agents, PRs and CI jobs on one PGX runtime, each creating, migrating, testing and dropping its own database +

## Benchmarks Measured on a 4 vCPU / 8 GB / $8.50-a-month VPS with ZFS block cloning. The load generator ran on the same CPUs, so these are architecture numbers, -not capacity limits. Every figure has a script and a committed raw result -([`benchmarks/pgx/linux/README.md`](benchmarks/pgx/linux/README.md)). +not capacity limits. -### Density +```text +~6.2 ms warm database mint, p50 (cold copy-on-write clone: 74 ms) +~70 KB memory per untouched idle database (~128 KB once queried) +128–155 MB one runtime holding 1,000 idle databases; 213 MB after all were used +111 MB runtime memory after 30,000 create → use → drop lifecycles +~674 ms copy-on-write branch of a 10 GB template, ~13.6 MB new storage +0 result differences on PostgreSQL's src/test/regress (219 / 231 byte-exact vs PG 18.6) +36 / 36 framework runs: Rails 8.1, Django 5.2, Prisma 6, SQLAlchemy 2, node-postgres +``` -| | PGX | -|---|---| -| 1000 idle databases in one runtime | **128–155 MB** in total | -| 1000 databases, each queried once | **213 MB** | -| Per idle database | ~70 KB untouched, ~128 KB once queried | -| 1000 databases with 100 active | 283 MB | -| Idle CPU with 1000 databases | 0.33–0.43 % of one core | +PGX is not trying to beat Postgres on single queries. Short statements are +about 0.05–0.15 ms slower than PostgreSQL 18, large single-threaded scans +1.5–2.2× slower, COPY and 4-client throughput equal; whole framework +lifecycles take 0–15 % longer. Transactional behaviour follows PostgreSQL, +including transactional DDL, savepoints and temporary tables. -For comparison, one idle PostgreSQL 18 server uses 16.6 MB. +These describe measured configurations and workloads, not a claim that +PGX is faster than PostgreSQL in general. The regression figure is +PostgreSQL's own test suite on one platform under the regress profile, +not a compatibility percentage for arbitrary SQL. Every figure has a +script and a committed raw result. The tables, the isolation results and +how to reproduce them: [`docs/pgx/benchmarks.md`](docs/pgx/benchmarks.md). +The full report: [`docs/pgx/linux-results.md`](docs/pgx/linux-results.md). -### Creating databases +## Architecture -| | p50 | p99 | -|---|---|---| -| Database from the warm pool | 6.2 ms | 9.4 ms | -| Cold database (copy-on-write clone of a 600-file template) | 74 ms | 102 ms | -| 500 simultaneous new-database requests | all served, 0 errors | drained in 12.6–13.8 s | +The central idea: **share everything immutable, pay for divergence.** It +applies to memory and to storage alike. -Times run from the client's connection to its first `SELECT 1` answer, -on the same host. +One process, one shared SQL engine, and one isolated database per client: +each agent, CI job or app connects over the ordinary Postgres wire protocol +and gets a database branched copy-on-write from a sealed template. The +engine, catalog, caches and warm pool are shared; only each database's own +changed blocks are written. Nothing is shared between databases. -### Branch storage (ZFS copy-on-write) +### 1,000 databases, one PGX process -| Template | Branch ready in | New physical storage | -|---|---|---| -| 1 GB | 135 ms | 2.3 MB | -| 2 GB | 198 ms | 3.7 MB | -| 5.2 GB | 417 ms | 7.0 MB | -| 10 GB | 674 ms | 13.6 MB | +One idle PostgreSQL server: 16.6 MB. One idle PGX database: about 70 KB. +Databases are isolated by name, roles and limits inside one process with a +thread per connection. A database costs its catalog caches, not a server: +databases cloned from the same template share identical relation-cache +entries, and idle databases are skipped by autovacuum. Measured on a +4 vCPU / 8 GB VM: -A branch of a 10 GB database is ready in under a second and initially uses -hundreds of times less physical storage than a full copy. +```text +one idle PostgreSQL 18 server 16.6 MB +one idle PGX database ~70 KB (~128 KB once it has been queried) +1,000 idle PGX databases 128–155 MB in one process +1,000 databases, each queried once 213 MB +1,000 databases, 100 active 283 MB +``` -### Running for a long time +[Benchmarks →](docs/pgx/benchmarks.md) -| | Runtime memory | -|---|---| -| After 10,000 create → use → drop lifecycles | 108 MB | -| After 30,000 lifecycles | 111 MB (~0.2 KB retained per lifecycle) | +### 1,000 databases on a MacBook in 46.6 seconds -### Compatibility +Not a cluster, not Docker, not the cloud: PGX on a 16 GB M1 Pro, cloning a +31 MB app database 1,000 times with `pgx clone`. All 1,000 clones in +46.6 s, 41 ms median per clone, 0 errors, and the engine idles at 20 MB +RSS with all 1,000 present. Under load it grows with the sessions, not the +databases: 340 MB at 100 concurrent readers, back to 20 MB when they +disconnect. -| | Result | -|---|---| -| PostgreSQL regression suite (`src/test/regress`, PG 18.6 expected output) | 219 / 231 byte-exact; 12 differ only in EXPLAIN plan text; **0 result differences**. Measured under the PGX profile with parallel workers and `synchronous_commit` restored (`configs/pgx-regress-overlay.conf`), the two settings the suite asserts on; under the shipped ephemeral profile those two settings alone make `select_parallel` and `stats` differ. One suite, one platform: not a compatibility score. | -| Real Rails 8.1 app (migrations, fixtures, model tests, schema change, `db:schema:load`) | passes; within 0–5 % of PostgreSQL 18 per phase | -| Django 5.2, Prisma 6, SQLAlchemy 2, node-postgres, Rails-shaped SQL | pass | -| All framework runs | **36 / 36** | - -Transactional behaviour follows PostgreSQL, including transactional DDL -and temporary tables: - -```sql -BEGIN; -CREATE TEMP TABLE pgx_test (id integer PRIMARY KEY, value text); -INSERT INTO pgx_test VALUES (1, 'PGX works'); -SAVEPOINT before_update; -UPDATE pgx_test SET value = 'changed' WHERE id = 1; -ROLLBACK TO SAVEPOINT before_update; -SELECT value FROM pgx_test WHERE id = 1; -- 'PGX works' -ROLLBACK; -SELECT to_regclass('pg_temp.pgx_test'); -- NULL: the table was rolled back +

+ 1,000 databases in one PGX runtime +

+ +### Mint-on-connect + +Connecting to `