From e554775bc2eeecc04355aedc5457a22a2b89bb7d Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Thu, 15 Jan 2026 20:59:43 -0800 Subject: [PATCH 1/6] Update readme --- README.md | 252 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 239 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 50c43467..16c9c4c2 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ helm repo add pgdogdev https://helm.pgdog.dev helm install pgdog pgdogdev/pgdog ``` -### Docker +### Try in Docker You can try PgDog quickly using Docker. Install [Docker Compose](https://docs.docker.com/compose/) and run: @@ -56,38 +56,264 @@ so we recommend you use OpenMetrics for monitoring. Example Datadog configuratio ## Features +### Transaction pooling + +Like PgBouncer, PgDog supports transaction (and session) pooling, allowing +100,000s of clients to use just a few PostgreSQL server connections. + +📘 **[Transactions](https://docs.pgdog.dev/features/transaction-mode)** + ### Load balancer -PgDog is an application layer (OSI Level 7) load balancer for PostgreSQL. It understands the Postgres protocol, can proxy multiple replicas (and primary) and distributes transactions evenly between databases. It supports multiple strategies, like round robin, random and least active connections. PgDog can also inspect queries and send `SELECT` queries to replicas, and all others to the primary. This allows to proxy all databases behind a single PgDog deployment. +PgDog is an application layer (OSI Level 7) load balancer for PostgreSQL. It understands the Postgres protocol, can proxy multiple replicas (and primary) and distributes transactions evenly between databases. It supports multiple strategies, like round robin, random and least active connections. -📘 **[Load balancer](https://docs.pgdog.dev/features/load-balancer)** +📘 **[Load balancer](https://docs.pgdog.dev/features/load-balancer/)** -#### Healthchecks and failover +**Example**: + +```toml +[[databases]] +name = "prod" +host = "10.0.0.1" +role = "primary" + +[[databases]] +name = "prod" +host = "10.0.0.2" +role = "replica" +``` + +#### Healthchecks PgDog maintains a real-time list of healthy hosts. When a database fails a healthcheck, it's removed from the active rotation and queries are re-routed to other replicas. This works like an HTTP load balancer, except it's for your database. -Failover maximizes database availability and protects against bad network connections, temporary hardware failures or misconfiguration. +Healthchecks maximize database availability and protect against bad network connections, temporary hardware failures or misconfiguration. -📘 **[Healthchecks](https://docs.pgdog.dev/features/healthchecks)** +📘 **[Healthchecks](https://docs.pgdog.dev/features/load-balancer/healthchecks/)** -### Transaction pooling +#### Single endpoint -Like PgBouncer, PgDog supports transaction (and session) pooling, allowing -100,000s of clients to use just a few PostgreSQL server connections. +PgDog uses [`pg_query`](https://github.com/pganalyze/pg_query.rs), which bundles the PostgreSQL native parser. By parsing queries, PgDog can detect write queries (e.g. `INSERT`, `UPDATE`, `CREATE TABLE`, etc.) and send them to the primary, leaving the replicas to serve reads (`SELECT`). This allows applications to connect to one PgDog deployment for both reads and writes. -📘 **[Transactions](https://docs.pgdog.dev/features/transaction-mode)** +📘 **[Single endpoint](https://docs.pgdog.dev/features/load-balancer/#single-endpoint)** + +##### Transactions + +Transactions can execute multiple statements, so in a primary & replica configuration, PgDog routes them to the primary. Clients however can indicate a transaction is read-only, in which case PgDog will send it to a replica, for example: + +```postgresql +BEGIN READ ONLY; +SELECT * FROM users LIMIT 1; +COMMIT; +``` + +📘 **[Load balancer + Transactions](https://docs.pgdog.dev/features/load-balancer/transactions/)** + +#### Failover + +PgDog keeps track of Postgres replication and can automatically redirect writes to a different database if a replica is promoted. This doesn't replace tools like Patroni which actually orchestrate failovers, but you can use PgDog alongside Patroni (or AWS RDS, or any other managed Postgres host), to gracefully failover live traffic. + +📘 **[Failover](https://docs.pgdog.dev/features/load-balancer/replication-failover/)** + +**Example**: + +To enable failover, set all database `role` attributes to `auto`: + +```toml +[general] +lsn_check_delay = 0 + +[[databases]] +name = "prod" +host = "10.0.0.1" +role = "auto" + +[[databases]] +name = "prod" +host = "10.0.0.2" +role = "auto" +``` ### Sharding -PgDog is able to handle databases with multiple shards by routing queries automatically to one or more shards. Using the native PostgreSQL parser, PgDog understands queries, extracts sharding keys and determines the best routing strategy. For cross-shard queries, PgDog assembles and transforms results in memory, sending them all to the client as if they are coming from a single database. +PgDog is able to handle databases with multiple shards by routing queries automatically to one or more databases. By using the PostgreSQL parser, PgDog understands queries, extracts sharding keys and determines the best routing strategy for each query. + +For cross-shard queries, PgDog assembles and transforms results in memory, sending them all to the client as if they are coming from a single database. + +**Example** + +```toml +[[databases]] +name = "prod" +host = "10.0.0.1" +shard = 0 + +[[databases]] +name = "prod" +host = "10.0.0.2" +shard = 1 +``` 📘 **[Sharding](https://docs.pgdog.dev/features/sharding/)** + +#### Sharding functions + +PgDog has two main sharding algorithms: + +1. Using PostgreSQL partition functions (`HASH`, `LIST`, `RANGE`) +2. Using schemas + +##### Partition-based sharding + +Partition-based sharding functions are taken directly from Postgres source code. This choice intentionally allows to shard data both with PgDog and with Postgres [foreign tables](https://www.postgresql.org/docs/current/sql-createforeigntable.html) and [`postgres_fdw`](https://www.postgresql.org/docs/current/postgres-fdw.html), which should help with migrating over to PgDog. + +**Examples** + +The `PARTITION BY HASH` algorithm is used by default: + +```toml +[[sharded_tables]] +database = "prod" +column = "user_id" +``` + +List-based sharding (analogous to `PARTITION BY LIST`) can be configured as follows: + +```toml +[[sharded_tables]] +database = "prod" +column = "user_id" + +[[sharded_mapping]] +database = "prod" +column = "user_id" +values = [1, 2, 3, 4] +shard = 0 + +[[sharded_mapping]] +database = "prod" +column = "user_id" +values = [5, 6, 7, 8] +shard = 1 +``` + +For range-based sharding, replace `values` with a range, e.g.: + +```toml +start = 0 +end = 5 +``` + +📘 **[Sharding functions](https://docs.pgdog.dev/features/sharding/sharding-functions/)** + +##### Schema-based sharding + +Schema-based sharding works on the basis of PostgreSQL schemas. Tables under the same schema are placed on the same shard and all queries that refer to those tables are routed to that shard automatically. + +**Example** + +```toml +[[sharded_schemas]] +database = "prod" +name = "customer_a" +shard = 0 + +[[sharded_schemas]] +database = "prod" +name = "customer_b" +shard = 1 +``` + +Queries that refer to `customer_a` schema will be sent to shard 0, for example: + +```postgresql +INSERT INTO customer_a.orders (id, user_id, amount) +VALUES ($1, $2, $3); +``` + +Tables have to be fully qualified or the schema must be set in the `search_path` session variable: + +```postgresql +SET search_path TO public, customer_a; +-- All subsequent queries will be sent to shard 0. +SELECT * FROM orders LIMIT 1; +``` + +You can set the `search_path` for the duration of a single transaction, using `SET LOCAL`, ensuring only that transaction is sent to the desired shard. + +📘 **[Schema-based sharding](https://docs.pgdog.dev/configuration/pgdog.toml/sharded_schemas/)** + +#### Direct-to-shard queries + +Queries that contain a sharding key are sent to one database only. This is the best case scenario for sharded databases, since the load is uniformly distributed across the cluster. + +**Example**: + +```postgresql +-- user_id is the sharding key. +SELECT * FROM users WHERE user_id = $1; +``` + +📘 **[Direct-to-shard queries](https://docs.pgdog.dev/features/sharding/query-routing/)** + +#### Cross-shard queries + +Queries with multiple sharding keys or without one are sent to all databases and results are post-processed and assembled in memory. PgDog then sends the final result to the client. + +📘 **[Cross-shard queries](https://docs.pgdog.dev/features/sharding/cross-shard-queries/)** + +Currently, support for certain SQL features in cross-shard queries is limited. However, the list of supported ones keeps growing: + +| Feature | Supported | Notes | +|-|-|-| +| Aggregates | Partial | `count`, `min`, `max`, `stddev`, `variance`, `sum`, `avg` are supported. | +| `ORDER BY` | Partial | Column in `ORDER BY` clause must be present in the result set. | +| `GROUP BY` | Partial | Same as `ORDER BY`, referenced columns must be present in result set. | +| Multi-tuple `INSERT` | Supported | | +| Sharding key `UPDATE` | Supported | | +| Subqueries | No | The same subquery is executed on all shards. | +| CTEs | No | The same CTE is executed on all shards. | + +- 📘 **[`SELECT`](https://docs.pgdog.dev/features/sharding/cross-shard-queries/select/)** +- 📘 **[`INSERT`](https://docs.pgdog.dev/features/sharding/cross-shard-queries/insert/)** +- 📘 **[`UPDATE` and `DELETE`](https://docs.pgdog.dev/features/sharding/cross-shard-queries/update/)** +- 📘 **[DDL](https://docs.pgdog.dev/features/sharding/cross-shard-queries/ddl/)** + #### Using `COPY` -PgDog ships with a text/CSV parser and can split `COPY` commands between all shards automatically. This allows clients to ingest data into sharded PostgreSQL without preprocessing. +PgDog ships with a text, CSV & binary `COPY` parser and can split rows sent via `COPY` command between all shards automatically. This allows clients to ingest data into sharded PostgreSQL without preprocessing. + +📘 **[Copy](https://docs.pgdog.dev/features/sharding/cross-shard-queries/copy/)** + +#### Consistency (two-phase transactions) + +To make sure cross-shard writes are atomic, PgDog supports Postgres' [two-phase transactions](https://www.postgresql.org/docs/current/two-phase.html). When enabled, PgDog will handle `COMMIT` statements sent by clients and execute the 2pc exchange on their behalf: + +```postgresql +PREPARE TRANSACTION '__pgdog_unique_id'; +COMMIT PREPARED '__pgdog_unique_id'; +``` + +In case the client disconnects or Postgres crashes during the 2pc exchange, PgDog will automatically rollback the prepared transaction: + +```postgresql +ROLLBACK PREPARED '__pgdog_unique_id'; +``` + +📘 **[Two-phase commit](https://docs.pgdog.dev/features/sharding/2pc/)** + +#### Unique identifiers + +While applications can use `UUID` (v4 and now v7) to generate unique primary keys, PgDog supports generating unique `BIGINT` identifiers, without using a sequence: + +```postgresql +SELECT pgdog.unique_id(); +``` + +This uses the Snowflake-like timestamp-based algorithm and can produce millions of unique numbers per second. -📘 **[Copy](https://docs.pgdog.dev/features/sharding/copy/)** +📘 **[Unique IDs](https://docs.pgdog.dev/features/sharding/unique-ids/)** #### Re-sharding From 81f7355b3a990a7e2bb288da00e261ab1259f5fb Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Thu, 15 Jan 2026 21:02:22 -0800 Subject: [PATCH 2/6] save --- README.md | 18 +++++++++--------- pgdog/tests/vector/README.md | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 16c9c4c2..82c42f4d 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ PgDog uses [`pg_query`](https://github.com/pganalyze/pg_query.rs), which bundles Transactions can execute multiple statements, so in a primary & replica configuration, PgDog routes them to the primary. Clients however can indicate a transaction is read-only, in which case PgDog will send it to a replica, for example: -```postgresql +```sql BEGIN READ ONLY; SELECT * FROM users LIMIT 1; COMMIT; @@ -227,14 +227,14 @@ shard = 1 Queries that refer to `customer_a` schema will be sent to shard 0, for example: -```postgresql +```sql INSERT INTO customer_a.orders (id, user_id, amount) VALUES ($1, $2, $3); ``` Tables have to be fully qualified or the schema must be set in the `search_path` session variable: -```postgresql +```sql SET search_path TO public, customer_a; -- All subsequent queries will be sent to shard 0. SELECT * FROM orders LIMIT 1; @@ -250,7 +250,7 @@ Queries that contain a sharding key are sent to one database only. This is the b **Example**: -```postgresql +```sql -- user_id is the sharding key. SELECT * FROM users WHERE user_id = $1; ``` @@ -290,14 +290,14 @@ PgDog ships with a text, CSV & binary `COPY` parser and can split rows sent via To make sure cross-shard writes are atomic, PgDog supports Postgres' [two-phase transactions](https://www.postgresql.org/docs/current/two-phase.html). When enabled, PgDog will handle `COMMIT` statements sent by clients and execute the 2pc exchange on their behalf: -```postgresql +```sql PREPARE TRANSACTION '__pgdog_unique_id'; COMMIT PREPARED '__pgdog_unique_id'; ``` In case the client disconnects or Postgres crashes during the 2pc exchange, PgDog will automatically rollback the prepared transaction: -```postgresql +```sql ROLLBACK PREPARED '__pgdog_unique_id'; ``` @@ -307,7 +307,7 @@ ROLLBACK PREPARED '__pgdog_unique_id'; While applications can use `UUID` (v4 and now v7) to generate unique primary keys, PgDog supports generating unique `BIGINT` identifiers, without using a sequence: -```postgresql +```sql SELECT pgdog.unique_id(); ``` @@ -370,7 +370,7 @@ database = "pgdog" If you'd like to try this out, you can set it up like so: -```postgresql +```sql CREATE DATABASE pgdog; CREATE USER pgdog PASSWORD 'pgdog' LOGIN; ``` @@ -408,7 +408,7 @@ password = "pgdog" And finally, to make it work locally, create the required databases: -```postgresql +```sql CREATE DATABASE shard_0; CREATE DATABASE shard_1; diff --git a/pgdog/tests/vector/README.md b/pgdog/tests/vector/README.md index 621e4b0c..37e452f2 100644 --- a/pgdog/tests/vector/README.md +++ b/pgdog/tests/vector/README.md @@ -6,7 +6,7 @@ This demo uses [Cohere/wikipedia](https://huggingface.co/datasets/Cohere/wikiped Install [pgvector](https://github.com/pgvector/pgvector) into your shards. Make sure to run: -```postgresql +```sql CREATE EXTENSION vector; ``` From c7bd60f58065f13602fa0c4ba538bee5325dfdf5 Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Thu, 15 Jan 2026 21:05:59 -0800 Subject: [PATCH 3/6] update --- README.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 82c42f4d..5f271434 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ PgDog uses [`pg_query`](https://github.com/pganalyze/pg_query.rs), which bundles Transactions can execute multiple statements, so in a primary & replica configuration, PgDog routes them to the primary. Clients however can indicate a transaction is read-only, in which case PgDog will send it to a replica, for example: -```sql +```postgresql BEGIN READ ONLY; SELECT * FROM users LIMIT 1; COMMIT; @@ -166,7 +166,7 @@ PgDog has two main sharding algorithms: ##### Partition-based sharding -Partition-based sharding functions are taken directly from Postgres source code. This choice intentionally allows to shard data both with PgDog and with Postgres [foreign tables](https://www.postgresql.org/docs/current/sql-createforeigntable.html) and [`postgres_fdw`](https://www.postgresql.org/docs/current/postgres-fdw.html), which should help with migrating over to PgDog. +Partition-based sharding functions are taken directly from Postgres source code. This choice intetionally allows to shard data both with PgDog and with Postgres [foreign tables](https://www.postgresql.org/docs/current/sql-createforeigntable.html) and [`postgres_fdw`](https://www.postgresql.org/docs/current/postgres-fdw.html), which should help with migrating over to PgDog. **Examples** @@ -198,7 +198,7 @@ values = [5, 6, 7, 8] shard = 1 ``` -For range-based sharding, replace `values` with a range, e.g.: +For range-based sharding, replace `values` with a range, e.g.,: ```toml start = 0 @@ -227,14 +227,14 @@ shard = 1 Queries that refer to `customer_a` schema will be sent to shard 0, for example: -```sql +```postgresql INSERT INTO customer_a.orders (id, user_id, amount) VALUES ($1, $2, $3); ``` Tables have to be fully qualified or the schema must be set in the `search_path` session variable: -```sql +```postgresql SET search_path TO public, customer_a; -- All subsequent queries will be sent to shard 0. SELECT * FROM orders LIMIT 1; @@ -250,7 +250,7 @@ Queries that contain a sharding key are sent to one database only. This is the b **Example**: -```sql +```postgresql -- user_id is the sharding key. SELECT * FROM users WHERE user_id = $1; ``` @@ -259,19 +259,19 @@ SELECT * FROM users WHERE user_id = $1; #### Cross-shard queries -Queries with multiple sharding keys or without one are sent to all databases and results are post-processed and assembled in memory. PgDog then sends the final result to the client. +Queries with multiple sharding keys or without one are sent to all databases are results are post-processed and assembled in memory. PgDog then sends the final result to the client. 📘 **[Cross-shard queries](https://docs.pgdog.dev/features/sharding/cross-shard-queries/)** -Currently, support for certain SQL features in cross-shard queries is limited. However, the list of supported ones keeps growing: +Currently, support for certain SQL features in cross-shard queries is limited. Howver, the list of supported ones keeps growing: | Feature | Supported | Notes | |-|-|-| | Aggregates | Partial | `count`, `min`, `max`, `stddev`, `variance`, `sum`, `avg` are supported. | | `ORDER BY` | Partial | Column in `ORDER BY` clause must be present in the result set. | | `GROUP BY` | Partial | Same as `ORDER BY`, referenced columns must be present in result set. | -| Multi-tuple `INSERT` | Supported | | -| Sharding key `UPDATE` | Supported | | +| Multi-tuple `INSERT` | Supported | PgDog generates one statement per tuple and executes them automatically. | +| Sharding key `UPDATE` | Supported | PgDog generates a `SELECT`, `INSERT` and `DELETE` statements and execute them automatically. | | Subqueries | No | The same subquery is executed on all shards. | | CTEs | No | The same CTE is executed on all shards. | @@ -288,16 +288,16 @@ PgDog ships with a text, CSV & binary `COPY` parser and can split rows sent via #### Consistency (two-phase transactions) -To make sure cross-shard writes are atomic, PgDog supports Postgres' [two-phase transactions](https://www.postgresql.org/docs/current/two-phase.html). When enabled, PgDog will handle `COMMIT` statements sent by clients and execute the 2pc exchange on their behalf: +To make sure cross-shard writes are atomic, PgDog supports Postgres' [two-phase transactions](https://www.postgresql.org/docs/current/two-phase.html). When enabled, PgDog will handle `COMMIT` statements sent by clients and execute the 2pc exhange on their behalf: -```sql +```postgresql PREPARE TRANSACTION '__pgdog_unique_id'; COMMIT PREPARED '__pgdog_unique_id'; ``` In case the client disconnects or Postgres crashes during the 2pc exchange, PgDog will automatically rollback the prepared transaction: -```sql +```postgresql ROLLBACK PREPARED '__pgdog_unique_id'; ``` @@ -307,7 +307,7 @@ ROLLBACK PREPARED '__pgdog_unique_id'; While applications can use `UUID` (v4 and now v7) to generate unique primary keys, PgDog supports generating unique `BIGINT` identifiers, without using a sequence: -```sql +```postgresql SELECT pgdog.unique_id(); ``` @@ -370,7 +370,7 @@ database = "pgdog" If you'd like to try this out, you can set it up like so: -```sql +```postgresql CREATE DATABASE pgdog; CREATE USER pgdog PASSWORD 'pgdog' LOGIN; ``` @@ -408,7 +408,7 @@ password = "pgdog" And finally, to make it work locally, create the required databases: -```sql +```postgresql CREATE DATABASE shard_0; CREATE DATABASE shard_1; From f5f2120adc15ec4c5f260a39af28242a7f3ccb64 Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Thu, 15 Jan 2026 21:09:37 -0800 Subject: [PATCH 4/6] save --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5f271434..8a75d857 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ PgDog is an application layer (OSI Level 7) load balancer for PostgreSQL. It und 📘 **[Load balancer](https://docs.pgdog.dev/features/load-balancer/)** -**Example**: +**Example** ```toml [[databases]] @@ -107,7 +107,7 @@ SELECT * FROM users LIMIT 1; COMMIT; ``` -📘 **[Load balancer + Transactions](https://docs.pgdog.dev/features/load-balancer/transactions/)** +📘 **[Load balancer & transactions](https://docs.pgdog.dev/features/load-balancer/transactions/)** #### Failover @@ -115,7 +115,7 @@ PgDog keeps track of Postgres replication and can automatically redirect writes 📘 **[Failover](https://docs.pgdog.dev/features/load-balancer/replication-failover/)** -**Example**: +**Example** To enable failover, set all database `role` attributes to `auto`: From e739d4d6a4afbea80b776fe47c92a6f266e46fa9 Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Thu, 15 Jan 2026 21:10:09 -0800 Subject: [PATCH 5/6] save --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 8a75d857..d4d07b32 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ PgDog uses [`pg_query`](https://github.com/pganalyze/pg_query.rs), which bundles Transactions can execute multiple statements, so in a primary & replica configuration, PgDog routes them to the primary. Clients however can indicate a transaction is read-only, in which case PgDog will send it to a replica, for example: -```postgresql +```sql BEGIN READ ONLY; SELECT * FROM users LIMIT 1; COMMIT; @@ -227,14 +227,14 @@ shard = 1 Queries that refer to `customer_a` schema will be sent to shard 0, for example: -```postgresql +```sql INSERT INTO customer_a.orders (id, user_id, amount) VALUES ($1, $2, $3); ``` Tables have to be fully qualified or the schema must be set in the `search_path` session variable: -```postgresql +```sql SET search_path TO public, customer_a; -- All subsequent queries will be sent to shard 0. SELECT * FROM orders LIMIT 1; @@ -250,7 +250,7 @@ Queries that contain a sharding key are sent to one database only. This is the b **Example**: -```postgresql +```sql -- user_id is the sharding key. SELECT * FROM users WHERE user_id = $1; ``` @@ -290,14 +290,14 @@ PgDog ships with a text, CSV & binary `COPY` parser and can split rows sent via To make sure cross-shard writes are atomic, PgDog supports Postgres' [two-phase transactions](https://www.postgresql.org/docs/current/two-phase.html). When enabled, PgDog will handle `COMMIT` statements sent by clients and execute the 2pc exhange on their behalf: -```postgresql +```sql PREPARE TRANSACTION '__pgdog_unique_id'; COMMIT PREPARED '__pgdog_unique_id'; ``` In case the client disconnects or Postgres crashes during the 2pc exchange, PgDog will automatically rollback the prepared transaction: -```postgresql +```sql ROLLBACK PREPARED '__pgdog_unique_id'; ``` @@ -307,7 +307,7 @@ ROLLBACK PREPARED '__pgdog_unique_id'; While applications can use `UUID` (v4 and now v7) to generate unique primary keys, PgDog supports generating unique `BIGINT` identifiers, without using a sequence: -```postgresql +```sql SELECT pgdog.unique_id(); ``` @@ -370,7 +370,7 @@ database = "pgdog" If you'd like to try this out, you can set it up like so: -```postgresql +```sql CREATE DATABASE pgdog; CREATE USER pgdog PASSWORD 'pgdog' LOGIN; ``` @@ -408,7 +408,7 @@ password = "pgdog" And finally, to make it work locally, create the required databases: -```postgresql +```sql CREATE DATABASE shard_0; CREATE DATABASE shard_1; From b10685aa77606391ff28aff02399b9578bb991cd Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Thu, 15 Jan 2026 23:16:16 -0800 Subject: [PATCH 6/6] readme --- README.md | 221 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 139 insertions(+), 82 deletions(-) diff --git a/README.md b/README.md index d4d07b32..76ec1a94 100644 --- a/README.md +++ b/README.md @@ -33,10 +33,10 @@ You can try PgDog quickly using Docker. Install [Docker Compose](https://docs.do docker-compose up ``` -It will take a few minutes to build PgDog from source and launch the containers. Once started, you can connect to PgDog with psql (or any other PostgreSQL client): +Once started, you can connect to PgDog with psql or any other PostgreSQL client: ``` -PGPASSWORD=postgres psql -h 127.0.0.1 -p 6432 -U postgres gssencmode=disable +PGPASSWORD=postgres psql -h 127.0.0.1 -p 6432 -U postgres ``` The demo comes with 3 shards and 2 sharded tables: @@ -49,28 +49,70 @@ SELECT * FROM users WHERE id = 1; SELECT * FROM payments WHERE user_id = 1; ``` -### Monitoring +## Features -PgDog exposes both the standard PgBouncer-style admin database and an OpenMetrics endpoint. The admin database isn't 100% compatible, -so we recommend you use OpenMetrics for monitoring. Example Datadog configuration and dashboard are [included](examples/datadog). +📘 **[Configuration](https://docs.pgdog.dev/configuration/)** -## Features +All PgDog features are configurable and can be turned on and off. PgDog requires 2 configuration files to operate: + +1. `pgdog.toml`: hosts, sharding configuration, and other settings +2. `users.toml`: usernames and passwords + +### Example + +Most options have reasonable defaults, so a basic configuration for a single user +and database running on the same machine is pretty short: + +**`pgdog.toml`** + +```toml +[general] +port = 6432 +default_pool_size = 10 + +[[databases]] +name = "pgdog" +host = "127.0.0.1" +``` + +**`users.toml`** + +```toml +[[users]] +name = "alice" +database = "pgdog" +password = "hunter2" +``` + +If a database in `pgdog.toml` doesn't have a user in `users.toml`, the connection pool for that database will not be created and users won't be able to connect to it. + +If you'd like to try it out, configure the database and user like so: + +```sql +CREATE DATABASE pgdog; +CREATE USER pgdog PASSWORD 'pgdog' LOGIN; +``` ### Transaction pooling +📘 **[Transactions](https://docs.pgdog.dev/features/transaction-mode)** + Like PgBouncer, PgDog supports transaction (and session) pooling, allowing -100,000s of clients to use just a few PostgreSQL server connections. +thousands of clients to use just a few PostgreSQL server connections. -📘 **[Transactions](https://docs.pgdog.dev/features/transaction-mode)** +Unlike PgBouncer, PgDog can parse and handle `SET` statements and startup options, ensuring session state is set correctly when sharing server connections between clients with different parameters. ### Load balancer -PgDog is an application layer (OSI Level 7) load balancer for PostgreSQL. It understands the Postgres protocol, can proxy multiple replicas (and primary) and distributes transactions evenly between databases. It supports multiple strategies, like round robin, random and least active connections. - 📘 **[Load balancer](https://docs.pgdog.dev/features/load-balancer/)** +PgDog is an application layer (OSI Level 7) load balancer for PostgreSQL. It understands the Postgres protocol, can proxy multiple replicas (and primary) and distributes transactions evenly between databases. The load balancer supports 3 strategies: round robin, random and least active connections. + + **Example** +The load balancer is enabled automatically when a database has more than one host: + ```toml [[databases]] name = "prod" @@ -83,41 +125,50 @@ host = "10.0.0.2" role = "replica" ``` -#### Healthchecks +#### Health checks -PgDog maintains a real-time list of healthy hosts. When a database fails a healthcheck, it's removed from the active rotation and queries are re-routed to other replicas. This works like an HTTP load balancer, except it's for your database. +📘 **[Healthchecks](https://docs.pgdog.dev/features/load-balancer/healthchecks/)** -Healthchecks maximize database availability and protect against bad network connections, temporary hardware failures or misconfiguration. -📘 **[Healthchecks](https://docs.pgdog.dev/features/load-balancer/healthchecks/)** +PgDog maintains a real-time list of healthy hosts. When a database fails a health check, it's removed from the active rotation and queries are re-routed to other replicas. This works like an HTTP load balancer, except it's for your database. + +Health checks maximize database availability and protect against bad network connections, temporary hardware failures or misconfiguration. -#### Single endpoint -PgDog uses [`pg_query`](https://github.com/pganalyze/pg_query.rs), which bundles the PostgreSQL native parser. By parsing queries, PgDog can detect write queries (e.g. `INSERT`, `UPDATE`, `CREATE TABLE`, etc.) and send them to the primary, leaving the replicas to serve reads (`SELECT`). This allows applications to connect to one PgDog deployment for both reads and writes. +#### Single endpoint 📘 **[Single endpoint](https://docs.pgdog.dev/features/load-balancer/#single-endpoint)** + +PgDog uses [`pg_query`](https://github.com/pganalyze/pg_query.rs), which includes the PostgreSQL native parser. By parsing queries, PgDog can detect writes (e.g. `INSERT`, `UPDATE`, `CREATE TABLE`, etc.) and send them to the primary, leaving the replicas to serve reads (`SELECT`). This allows applications to connect to the same PgDog deployment for both reads and writes. + + ##### Transactions -Transactions can execute multiple statements, so in a primary & replica configuration, PgDog routes them to the primary. Clients however can indicate a transaction is read-only, in which case PgDog will send it to a replica, for example: +📘 **[Load balancer & transactions](https://docs.pgdog.dev/features/load-balancer/transactions/)** + + +Transactions can execute multiple statements, so in a primary & replica configuration, PgDog routes them to the primary. Clients can indicate a transaction is read-only, in which case PgDog will send it to a replica: ```sql BEGIN READ ONLY; +-- This goes to a replica. SELECT * FROM users LIMIT 1; COMMIT; ``` -📘 **[Load balancer & transactions](https://docs.pgdog.dev/features/load-balancer/transactions/)** #### Failover -PgDog keeps track of Postgres replication and can automatically redirect writes to a different database if a replica is promoted. This doesn't replace tools like Patroni which actually orchestrate failovers, but you can use PgDog alongside Patroni (or AWS RDS, or any other managed Postgres host), to gracefully failover live traffic. - 📘 **[Failover](https://docs.pgdog.dev/features/load-balancer/replication-failover/)** + +PgDog monitors Postgres replication state and can automatically redirect writes to a different database if a replica is promoted. This doesn't replace tools like Patroni that actually orchestrate failovers. You can use PgDog alongside Patroni (or AWS RDS or other managed Postgres host), to gracefully failover live traffic. + + **Example** -To enable failover, set all database `role` attributes to `auto`: +To enable failover, set all database `role` attributes to `auto` and enable replication monitoring (`lsn_check_delay` setting): ```toml [general] @@ -136,12 +187,16 @@ role = "auto" ### Sharding -PgDog is able to handle databases with multiple shards by routing queries automatically to one or more databases. By using the PostgreSQL parser, PgDog understands queries, extracts sharding keys and determines the best routing strategy for each query. +📘 **[Sharding](https://docs.pgdog.dev/features/sharding/)** + +PgDog is able to manage databases with multiple shards. By using the PostgreSQL parser, PgDog extracts sharding keys and determines the best routing strategy for each query. -For cross-shard queries, PgDog assembles and transforms results in memory, sending them all to the client as if they are coming from a single database. +For cross-shard queries, PgDog assembles and transforms results in memory, sending all rows to the client as if they are coming from a single database. **Example** +Configuring multiple hosts for the same database with different shard numbers (`shard` setting) enables sharding: + ```toml [[databases]] name = "prod" @@ -154,23 +209,24 @@ host = "10.0.0.2" shard = 1 ``` -📘 **[Sharding](https://docs.pgdog.dev/features/sharding/)** - +Note: read below for how to configure query routing. At least one sharded table is required for sharding to work as expected. #### Sharding functions +📘 **[Sharding functions](https://docs.pgdog.dev/features/sharding/sharding-functions/)** + PgDog has two main sharding algorithms: -1. Using PostgreSQL partition functions (`HASH`, `LIST`, `RANGE`) +1. PostgreSQL partition functions (`HASH`, `LIST`, `RANGE`) 2. Using schemas ##### Partition-based sharding -Partition-based sharding functions are taken directly from Postgres source code. This choice intetionally allows to shard data both with PgDog and with Postgres [foreign tables](https://www.postgresql.org/docs/current/sql-createforeigntable.html) and [`postgres_fdw`](https://www.postgresql.org/docs/current/postgres-fdw.html), which should help with migrating over to PgDog. +Partition-based sharding functions are taken directly from Postgres source code. This choice intentionally allows to shard data both with PgDog and with Postgres [foreign tables](https://www.postgresql.org/docs/current/sql-createforeigntable.html) and [`postgres_fdw`](https://www.postgresql.org/docs/current/postgres-fdw.html). **Examples** -The `PARTITION BY HASH` algorithm is used by default: +The `PARTITION BY HASH` algorithm is used by default when configuring sharded tables: ```toml [[sharded_tables]] @@ -178,13 +234,15 @@ database = "prod" column = "user_id" ``` -List-based sharding (analogous to `PARTITION BY LIST`) can be configured as follows: +List-based sharding (same as `PARTITION BY LIST` in Postgres) can be configured as follows: ```toml +# Sharded table definition still required. [[sharded_tables]] database = "prod" column = "user_id" +# Value-specific shard mappings. [[sharded_mapping]] database = "prod" column = "user_id" @@ -198,21 +256,23 @@ values = [5, 6, 7, 8] shard = 1 ``` -For range-based sharding, replace `values` with a range, e.g.,: +For range-based sharding, replace the `values` setting with a range, for example: ```toml -start = 0 -end = 5 +start = 0 # include +end = 5 # exclusive ``` -📘 **[Sharding functions](https://docs.pgdog.dev/features/sharding/sharding-functions/)** - ##### Schema-based sharding +📘 **[Schema-based sharding](https://docs.pgdog.dev/configuration/pgdog.toml/sharded_schemas/)** + Schema-based sharding works on the basis of PostgreSQL schemas. Tables under the same schema are placed on the same shard and all queries that refer to those tables are routed to that shard automatically. **Example** +Configuring sharded schemas uses a different configuration from sharded tables: + ```toml [[sharded_schemas]] database = "prod" @@ -225,14 +285,14 @@ name = "customer_b" shard = 1 ``` -Queries that refer to `customer_a` schema will be sent to shard 0, for example: +Queries that refer tables in schema `customer_a` will be sent to shard 0. For example, a query that refers to a table by its fully-qualified name will be sent to one shard only: ```sql INSERT INTO customer_a.orders (id, user_id, amount) VALUES ($1, $2, $3); ``` -Tables have to be fully qualified or the schema must be set in the `search_path` session variable: +Alternatively, the schema name can be specified in the `search_path` session variable: ```sql SET search_path TO public, customer_a; @@ -240,12 +300,21 @@ SET search_path TO public, customer_a; SELECT * FROM orders LIMIT 1; ``` -You can set the `search_path` for the duration of a single transaction, using `SET LOCAL`, ensuring only that transaction is sent to the desired shard. +You can also set the `search_path` for the duration of a single transaction, using `SET LOCAL`, ensuring only that transaction is sent to the desired shard: -📘 **[Schema-based sharding](https://docs.pgdog.dev/configuration/pgdog.toml/sharded_schemas/)** +```sql +-- The entire transaction will be sent to shard 1. +BEGIN; +SET LOCAL search_path TO public, customer_b; +SELECT * FROM orders LIMIT 1; +COMMIT; +``` #### Direct-to-shard queries +📘 **[Direct-to-shard queries](https://docs.pgdog.dev/features/sharding/query-routing/)** + + Queries that contain a sharding key are sent to one database only. This is the best case scenario for sharded databases, since the load is uniformly distributed across the cluster. **Example**: @@ -255,15 +324,17 @@ Queries that contain a sharding key are sent to one database only. This is the b SELECT * FROM users WHERE user_id = $1; ``` -📘 **[Direct-to-shard queries](https://docs.pgdog.dev/features/sharding/query-routing/)** - #### Cross-shard queries -Queries with multiple sharding keys or without one are sent to all databases are results are post-processed and assembled in memory. PgDog then sends the final result to the client. +- 📘 **[Cross-shard queries](https://docs.pgdog.dev/features/sharding/cross-shard-queries/)** +- 📘 **[SELECT](https://docs.pgdog.dev/features/sharding/cross-shard-queries/select/)** +- 📘 **[INSERT](https://docs.pgdog.dev/features/sharding/cross-shard-queries/insert/)** +- 📘 **[UPDATE and DELETE](https://docs.pgdog.dev/features/sharding/cross-shard-queries/update/)** +- 📘 **[DDL](https://docs.pgdog.dev/features/sharding/cross-shard-queries/ddl/)** -📘 **[Cross-shard queries](https://docs.pgdog.dev/features/sharding/cross-shard-queries/)** +Queries with multiple sharding keys or without one are sent to all databases and results are post-processed and assembled in memory. PgDog then sends the final result to the client. -Currently, support for certain SQL features in cross-shard queries is limited. Howver, the list of supported ones keeps growing: +Currently, support for certain SQL features in cross-shard queries is limited. However, the list of supported ones keeps growing: | Feature | Supported | Notes | |-|-|-| @@ -275,59 +346,45 @@ Currently, support for certain SQL features in cross-shard queries is limited. H | Subqueries | No | The same subquery is executed on all shards. | | CTEs | No | The same CTE is executed on all shards. | -- 📘 **[`SELECT`](https://docs.pgdog.dev/features/sharding/cross-shard-queries/select/)** -- 📘 **[`INSERT`](https://docs.pgdog.dev/features/sharding/cross-shard-queries/insert/)** -- 📘 **[`UPDATE` and `DELETE`](https://docs.pgdog.dev/features/sharding/cross-shard-queries/update/)** -- 📘 **[DDL](https://docs.pgdog.dev/features/sharding/cross-shard-queries/ddl/)** #### Using `COPY` -PgDog ships with a text, CSV & binary `COPY` parser and can split rows sent via `COPY` command between all shards automatically. This allows clients to ingest data into sharded PostgreSQL without preprocessing. - 📘 **[Copy](https://docs.pgdog.dev/features/sharding/cross-shard-queries/copy/)** -#### Consistency (two-phase transactions) +PgDog has a text, CSV & binary parser and can split rows sent via `COPY` command between all shards automatically. This allows clients to ingest data into sharded PostgreSQL without preprocessing. -To make sure cross-shard writes are atomic, PgDog supports Postgres' [two-phase transactions](https://www.postgresql.org/docs/current/two-phase.html). When enabled, PgDog will handle `COMMIT` statements sent by clients and execute the 2pc exhange on their behalf: -```sql -PREPARE TRANSACTION '__pgdog_unique_id'; -COMMIT PREPARED '__pgdog_unique_id'; -``` +#### Consistency (two-phase commit) -In case the client disconnects or Postgres crashes during the 2pc exchange, PgDog will automatically rollback the prepared transaction: +📘 **[Two-phase commit](https://docs.pgdog.dev/features/sharding/2pc/)** + +To make sure cross-shard writes are atomic, PgDog supports Postgres' [two-phase transactions](https://www.postgresql.org/docs/current/two-phase.html). When enabled, PgDog handles `COMMIT` statements sent by clients by executing the 2pc exchange on their behalf: ```sql -ROLLBACK PREPARED '__pgdog_unique_id'; +PREPARE TRANSACTION '__pgdog_unique_id'; +COMMIT PREPARED '__pgdog_unique_id'; ``` -📘 **[Two-phase commit](https://docs.pgdog.dev/features/sharding/2pc/)** +In case the client disconnects or Postgres crashes, PgDog will automatically rollback the transaction if it's in phase I and commit it if it's in phase II. #### Unique identifiers -While applications can use `UUID` (v4 and now v7) to generate unique primary keys, PgDog supports generating unique `BIGINT` identifiers, without using a sequence: +📘 **[Unique IDs](https://docs.pgdog.dev/features/sharding/unique-ids/)** + +While applications can use `UUID` (v4 and now v7) to generate unique primary keys, PgDog supports creating unique `BIGINT` identifiers, without using a sequence: ```sql SELECT pgdog.unique_id(); ``` -This uses the Snowflake-like timestamp-based algorithm and can produce millions of unique numbers per second. - -📘 **[Unique IDs](https://docs.pgdog.dev/features/sharding/unique-ids/)** +This uses a timestamp-based algorithm, can produce millions of unique numbers per second and doesn't require an expensive cross-shard index to guarantee uniqueness. #### Re-sharding -PgDog understands the PostgreSQL logical replication protocol and can split data between databases in the background and without downtime. This allows to shard existing databases and add more shards to existing clusters in production, without impacting database operations. - 📘 **[Re-sharding](https://docs.pgdog.dev/features/sharding/resharding/)** -### Configuration +PgDog understands the PostgreSQL logical replication protocol and can orchestrate data splits between databases, in the background and without downtime. This allows to shard existing databases and add more shards to existing clusters in production, without impacting database operations. -PgDog is highly configurable and most aspects of its operation can be tweaked at runtime, without having -to restart the process or break connections. If you've used PgBouncer (or PgCat) before, the options -will be familiar. If not, they are documented with examples. - -📘 **[Configuration](https://docs.pgdog.dev/configuration/)** ## Running PgDog locally @@ -348,8 +405,6 @@ PgDog has two configuration files: * `pgdog.toml` which contains general settings and PostgreSQL servers information * `users.toml` for users and passwords -Most options have reasonable defaults, so a basic configuration for a single user -and database running on the same machine is pretty short: **`pgdog.toml`** @@ -368,12 +423,10 @@ password = "pgdog" database = "pgdog" ``` -If you'd like to try this out, you can set it up like so: +### Monitoring -```sql -CREATE DATABASE pgdog; -CREATE USER pgdog PASSWORD 'pgdog' LOGIN; -``` +PgDog exposes both the standard PgBouncer-style admin database and an OpenMetrics endpoint. The admin database isn't 100% compatible, +so we recommend you use OpenMetrics for monitoring. Example Datadog configuration and dashboard are [included](examples/datadog). #### Try sharding @@ -393,9 +446,13 @@ name = "pgdog_sharded" host = "127.0.0.1" database_name = "shard_1" shard = 1 + +[[sharded_tables]] +database = "pgdog_sharded" +column = "user_id" ``` -Don't forget to specify a user: +Don't forget to configure a user: **`users.toml`** @@ -441,19 +498,19 @@ cargo run --release -- -d postgres://user:pass@localhost:5432/db1 -d postgres:// You can connect to PgDog with `psql` or any other PostgreSQL client: ```bash -psql "postgres://pgdog:pgdog@127.0.0.1:6432/pgdog?gssencmode=disable" +psql postgres://pgdog:pgdog@127.0.0.1:6432/pgdog ``` ## 🚦 Status 🚦 -PgDog is used in production and at scale. Most features are stable, while some are experimental. Check [documentation](https://docs.pgdog.dev/features/) for more details. +PgDog is used in production and at scale. Most features are stable, while some are experimental. Check [documentation](https://docs.pgdog.dev/features/) for more details. New sharding features are added almost weekly. ## Performance -PgDog does its best to minimize its impact on overall database performance. Using Rust and Tokio is a great start for a fast network proxy, but additional care is also taken to perform as few operations as possible while moving data between client and server sockets. Some benchmarks are provided to help set a baseline. - 📘 **[Architecture & benchmarks](https://docs.pgdog.dev/architecture/)** +PgDog is heavily optimized for performance. We use Rust, [Tokio](https://tokio.rs/), [bytes crate](https://docs.rs/bytes/latest/bytes/) to avoid unnecessary memory allocations, and profile for performance regressions on a regular basis. + ## License PgDog is free and open source software, licensed under the AGPL v3. While often misunderstood, this license is very permissive