Skip to content

Commit f1ac787

Browse files
authored
improve benchmarks (#114)
1 parent 64529c8 commit f1ac787

File tree

9 files changed

+233
-124
lines changed

9 files changed

+233
-124
lines changed

.github/workflows/bench-aws-fargate.yml

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,35 +14,30 @@ on:
1414
default: 'true'
1515
type: boolean
1616
fargate_cpu:
17-
description: 'Fargate CPU units (1024=1vCPU, 2048=2vCPU, 4096=4vCPU, 8192=8vCPU, 16384=16vCPU)'
17+
description: 'Fargate CPU units (8192=8vCPU, 16384=16vCPU)'
1818
required: false
1919
default: '16384'
2020
type: choice
2121
options:
22-
- '2048'
23-
- '4096'
2422
- '8192'
2523
- '16384'
2624
fargate_memory:
27-
description: 'Fargate memory in MB (must be compatible with CPU)'
25+
description: 'Fargate memory in MB (32GB, 64GB, 120GB)'
2826
required: false
2927
default: '32768'
3028
type: choice
3129
options:
32-
- '4096'
33-
- '8192'
34-
- '16384'
3530
- '32768'
3631
- '61440'
3732
- '122880'
3833
doorman_workers:
3934
description: 'Number of worker threads for pg_doorman and odyssey'
4035
required: false
41-
default: '4'
36+
default: '12'
4237
pgbench_jobs:
4338
description: 'Number of threads (-j) for pgbench (global override)'
4439
required: false
45-
default: ''
40+
default: '4'
4641

4742
env:
4843
REGISTRY: ghcr.io

benches/bench.feature

Lines changed: 112 additions & 51 deletions
Large diffs are not rendered by default.

documentation/docs/index.md

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,24 @@ title: Home
77

88
## PgDoorman: PostgreSQL Pooler
99

10-
PgDoorman is a stable and good alternative to [PgBouncer](https://www.pgbouncer.org/), [Odyssey](https://github.com/yandex/odyssey), or [PgCat](https://github.com/postgresml/pgcat) (based on it).
11-
It has created with the Unix philosophy in mind. Developing was focused on perfomance, efficience and reliability.
12-
Also, PgDoorman has the improved driver support for languages like Go (pgx), .NET (npgsql), and asynchronous drivers for Python and Node.js.
10+
PgDoorman is a stable and high-performance alternative to [PgBouncer](https://www.pgbouncer.org/), [Odyssey](https://github.com/yandex/odyssey), or [PgCat](https://github.com/postgresml/pgcat).
11+
It was created with the Unix philosophy in mind. Development focused on performance, efficiency, and reliability.
12+
Additionally, PgDoorman provides improved driver support for languages like Go (pgx), .NET (npgsql), and asynchronous drivers for Python and Node.js.
1313

1414
[:fontawesome-solid-download: Get PgDoorman {{ version }}](tutorials/installation.md){ .md-button .md-button--primary }
1515

16+
### Quick Start
17+
18+
Run PgDoorman instantly using Docker:
19+
20+
```bash
21+
docker run -p 6432:6432 \
22+
-v $(pwd)/pg_doorman.toml:/etc/pg_doorman/pg_doorman.toml \
23+
ghcr.io/ozontech/pg_doorman
24+
```
25+
26+
*For more details, see the [Installation Guide](tutorials/installation.md).*
27+
1628

1729
### Why not multi-PgBouncer?
1830

@@ -33,7 +45,7 @@ Some of the key differences include:
3345

3446
- Performance improvements compared to PgCat/PgBouncer/Odyssey.
3547
- Support for extended protocol with popular programming language drivers.
36-
- Enhanced monitoring metrics to improve visibility into database activity..
48+
- Enhanced monitoring metrics to improve visibility into database activity.
3749
- Careful resource management to avoid memory issues (`max_memory_usage`, `message_size_to_be_stream`).
3850
- SCRAM client/server authentication support.
3951
- [Gracefully binary upgrade](tutorials/binary-upgrade.md).

documentation/docs/tutorials/overview.md

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ title: Overview
88

99
PgDoorman is a high-performance PostgreSQL connection pooler based on PgCat. It acts as a middleware between your applications and PostgreSQL servers, efficiently managing database connections to improve performance and resource utilization.
1010

11+
```mermaid
12+
graph LR
13+
App1[Application A] --> Pooler(PgDoorman)
14+
App2[Application B] --> Pooler
15+
App3[Application C] --> Pooler
16+
Pooler --> DB[(PostgreSQL)]
17+
```
18+
1119
When an application connects to PgDoorman, it behaves exactly like a PostgreSQL server. Behind the scenes, PgDoorman either creates a new connection to the actual PostgreSQL server or reuses an existing connection from its pool, significantly reducing connection overhead.
1220

1321
## Key Benefits
@@ -21,23 +29,21 @@ When an application connects to PgDoorman, it behaves exactly like a PostgreSQL
2129

2230
To maintain proper transaction semantics while providing efficient connection pooling, PgDoorman supports multiple pooling modes:
2331

24-
### Session Pooling
32+
### Transaction Pooling
2533

26-
In session pooling mode:
34+
!!! success "Recommended for most use cases"
35+
In transaction pooling mode, a client is assigned a server connection only for the duration of a transaction. Once the transaction ends, the connection is released back into the pool.
2736

28-
- Each client is assigned a dedicated server connection for the entire duration of the client connection
29-
- The server connection remains exclusively allocated to that client until disconnection
30-
- After the client disconnects, the server connection is released back into the pool for reuse
31-
- This mode is ideal for applications that rely on session-level features like temporary tables or session variables
37+
- **High Efficiency**: Connections are shared between clients, allowing thousands of clients to share a small pool.
38+
- **Ideal for**: Applications with many short-lived connections or those that don't rely on session state.
3239

33-
### Transaction Pooling
40+
### Session Pooling
3441

35-
In transaction pooling mode:
42+
!!! info "Useful for specific legacy needs"
43+
In session pooling mode, each client is assigned a dedicated server connection for the entire duration of the client connection.
3644

37-
- A client is assigned a server connection only for the duration of a transaction
38-
- Once PgDoorman detects the end of a transaction, the server connection is immediately released back into the pool
39-
- This mode allows for higher connection efficiency as connections are shared between clients
40-
- Ideal for applications with many short-lived connections or those that don't rely on session state
45+
- **Exclusive Allocation**: The connection remains exclusively allocated to that client until disconnection.
46+
- **Support for Session Features**: Ideal for applications that rely on temporary tables or session variables.
4147

4248
## Administration
4349

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Troubleshooting
2+
3+
This guide helps you resolve common issues when using PgDoorman.
4+
5+
# TODO
6+
7+
---
8+
9+
!!! tip "Still having issues?"
10+
If you encounter a problem not listed here, please [open an issue on GitHub](https://github.com/ozontech/pg_doorman/issues).

documentation/mkdocs.yml

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,39 +44,49 @@ theme:
4444
- navigation.sections
4545
- navigation.indexes
4646
- navigation.top
47+
- navigation.breadcrumbs
48+
- navigation.expand
49+
- toc.follow
50+
- search.highlight
51+
- search.share
52+
- search.suggest
4753
markdown_extensions:
4854
- tables
4955
- attr_list
5056
- admonition
5157
- pymdownx.details
52-
- pymdownx.superfences
58+
- pymdownx.superfences:
59+
custom_fences:
60+
- name: mermaid
61+
class: mermaid
62+
format: !!python/name:pymdownx.superfences.fence_code_format
5363
- pymdownx.highlight:
5464
anchor_linenums: true
5565
line_spans: __span
5666
pygments_lang_class: true
5767
- pymdownx.inlinehilite
5868
- pymdownx.snippets
59-
- pymdownx.superfences
6069
- md_in_html
6170
- pymdownx.blocks.caption
6271
- pymdownx.emoji:
6372
emoji_index: !!python/name:material.extensions.emoji.twemoji
6473
emoji_generator: !!python/name:material.extensions.emoji.to_svg
6574
nav:
66-
- index.md
67-
- 'Getting started':
68-
- 'tutorials/overview.md'
69-
- 'tutorials/installation.md'
70-
- 'tutorials/basic-usage.md'
71-
- 'tutorials/binary-upgrade.md'
72-
- 'tutorials/patroni-proxy.md'
73-
- 'tutorials/contributing.md'
74-
- 'changelog.md'
75+
- Home: index.md
76+
- Overview: tutorials/overview.md
77+
- Installation: tutorials/installation.md
78+
- 'User Guide':
79+
- tutorials/basic-usage.md
80+
- tutorials/binary-upgrade.md
81+
- tutorials/patroni-proxy.md
82+
- tutorials/troubleshooting.md
83+
- Benchmarks: benchmarks.md
7584
- 'Reference':
76-
- 'reference/general.md'
77-
- 'reference/pool.md'
78-
- 'reference/prometheus.md'
79-
- benchmarks.md
85+
- reference/general.md
86+
- reference/pool.md
87+
- reference/prometheus.md
88+
- Changelog: changelog.md
89+
- Contributing: tutorials/contributing.md
8090
plugins:
8191
- search
8292
- autorefs

tests/bdd/README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,14 @@ cargo test --test bdd -- --tags @bench
6161

6262
You can parameterize benchmarks using environment variables:
6363

64-
- `BENCH_DOORMAN_WORKERS`: Number of worker threads for `pg_doorman` (default: 4)
65-
- `BENCH_ODYSSEY_WORKERS`: Number of workers for `odyssey` (default: 4)
64+
- `BENCH_DOORMAN_WORKERS`: Number of worker threads for `pg_doorman` (default: 12)
65+
- `BENCH_ODYSSEY_WORKERS`: Number of workers for `odyssey` (default: 12)
6666
- `BENCH_PGBENCH_JOBS`: Global number of threads (`-j`) for `pgbench`. If set, it overrides all specific job settings.
6767
- `BENCH_PGBENCH_JOBS_C1`: Number of threads for 1-client tests (default: 1)
68-
- `BENCH_PGBENCH_JOBS_C40`: Number of threads for 40-client tests (default: 2)
69-
- `BENCH_PGBENCH_JOBS_C80`: Number of threads for 80-client tests (default: 4)
68+
- `BENCH_PGBENCH_JOBS_C40`: Number of threads for 40-client tests (default: 4)
7069
- `BENCH_PGBENCH_JOBS_C120`: Number of threads for 120-client tests (default: 4)
70+
- `BENCH_PGBENCH_JOBS_C500`: Number of threads for 500-client tests (default: 4)
71+
- `BENCH_PGBENCH_JOBS_C10000`: Number of threads for 10,000-client tests (default: 4)
7172
- `FARGATE_CPU`: AWS Fargate CPU units (optional, for reporting)
7273
- `FARGATE_MEMORY`: AWS Fargate memory in MB (optional, for reporting)
7374

tests/bdd/pgbench_helper.rs

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,52 +1006,62 @@ pub async fn generate_benchmark_markdown_table(world: &mut DoormanWorld) {
10061006
let simple_configs: Vec<(&str, &str)> = vec![
10071007
("simple_c1", "1 client"),
10081008
("simple_c40", "40 clients"),
1009-
("simple_c80", "80 clients"),
10101009
("simple_c120", "120 clients"),
1010+
("simple_c500", "500 clients"),
1011+
("simple_c10000", "10,000 clients"),
10111012
("simple_connect_c1", "1 client + Reconnect"),
10121013
("simple_connect_c40", "40 clients + Reconnect"),
1013-
("simple_connect_c80", "80 clients + Reconnect"),
10141014
("simple_connect_c120", "120 clients + Reconnect"),
1015+
("simple_connect_c500", "500 clients + Reconnect"),
1016+
("simple_connect_c10000", "10,000 clients + Reconnect"),
10151017
("ssl_simple_c1", "1 client + SSL"),
10161018
("ssl_simple_c40", "40 clients + SSL"),
1017-
("ssl_simple_c80", "80 clients + SSL"),
10181019
("ssl_simple_c120", "120 clients + SSL"),
1020+
("ssl_simple_c500", "500 clients + SSL"),
1021+
("ssl_simple_c10000", "10,000 clients + SSL"),
10191022
];
10201023

10211024
// Extended Protocol tests
10221025
let extended_configs: Vec<(&str, &str)> = vec![
10231026
("extended_c1", "1 client"),
10241027
("extended_c40", "40 clients"),
1025-
("extended_c80", "80 clients"),
10261028
("extended_c120", "120 clients"),
1029+
("extended_c500", "500 clients"),
1030+
("extended_c10000", "10,000 clients"),
10271031
("extended_connect_c1", "1 client + Reconnect"),
10281032
("extended_connect_c40", "40 clients + Reconnect"),
1029-
("extended_connect_c80", "80 clients + Reconnect"),
10301033
("extended_connect_c120", "120 clients + Reconnect"),
1034+
("extended_connect_c500", "500 clients + Reconnect"),
1035+
("extended_connect_c10000", "10,000 clients + Reconnect"),
10311036
("ssl_extended_c1", "1 client + SSL"),
10321037
("ssl_extended_c40", "40 clients + SSL"),
1033-
("ssl_extended_c80", "80 clients + SSL"),
10341038
("ssl_extended_c120", "120 clients + SSL"),
1039+
("ssl_extended_c500", "500 clients + SSL"),
1040+
("ssl_extended_c10000", "10,000 clients + SSL"),
10351041
("ssl_connect_c1", "1 client + SSL + Reconnect"),
10361042
("ssl_connect_c40", "40 clients + SSL + Reconnect"),
1037-
("ssl_connect_c80", "80 clients + SSL + Reconnect"),
10381043
("ssl_connect_c120", "120 clients + SSL + Reconnect"),
1044+
("ssl_connect_c500", "500 clients + SSL + Reconnect"),
1045+
("ssl_connect_c10000", "10,000 clients + SSL + Reconnect"),
10391046
];
10401047

10411048
// Prepared Protocol tests
10421049
let prepared_configs: Vec<(&str, &str)> = vec![
10431050
("prepared_c1", "1 client"),
10441051
("prepared_c40", "40 clients"),
1045-
("prepared_c80", "80 clients"),
10461052
("prepared_c120", "120 clients"),
1053+
("prepared_c500", "500 clients"),
1054+
("prepared_c10000", "10,000 clients"),
10471055
("prepared_connect_c1", "1 client + Reconnect"),
10481056
("prepared_connect_c40", "40 clients + Reconnect"),
1049-
("prepared_connect_c80", "80 clients + Reconnect"),
10501057
("prepared_connect_c120", "120 clients + Reconnect"),
1058+
("prepared_connect_c500", "500 clients + Reconnect"),
1059+
("prepared_connect_c10000", "10,000 clients + Reconnect"),
10511060
("ssl_prepared_c1", "1 client + SSL"),
10521061
("ssl_prepared_c40", "40 clients + SSL"),
1053-
("ssl_prepared_c80", "80 clients + SSL"),
10541062
("ssl_prepared_c120", "120 clients + SSL"),
1063+
("ssl_prepared_c500", "500 clients + SSL"),
1064+
("ssl_prepared_c10000", "10,000 clients + SSL"),
10551065
];
10561066

10571067
let simple_table = generate_table(&simple_configs, &world.bench_results);
@@ -1068,11 +1078,11 @@ pub async fn generate_benchmark_markdown_table(world: &mut DoormanWorld) {
10681078
let doorman_workers = std::env::var("BENCH_DOORMAN_WORKERS")
10691079
.ok()
10701080
.filter(|s| !s.is_empty())
1071-
.unwrap_or_else(|| "4".to_string());
1081+
.unwrap_or_else(|| "12".to_string());
10721082
let odyssey_workers = std::env::var("BENCH_ODYSSEY_WORKERS")
10731083
.ok()
10741084
.filter(|s| !s.is_empty())
1075-
.unwrap_or_else(|| "4".to_string());
1085+
.unwrap_or_else(|| "12".to_string());
10761086
let pgbench_jobs = std::env::var("BENCH_PGBENCH_JOBS")
10771087
.ok()
10781088
.filter(|s| !s.is_empty());
@@ -1095,7 +1105,7 @@ pub async fn generate_benchmark_markdown_table(world: &mut DoormanWorld) {
10951105
if let Some(jobs) = pgbench_jobs {
10961106
env_info.push(format!("- **pgbench jobs**: {} (global override)", jobs));
10971107
} else {
1098-
env_info.push("- **pgbench jobs**: variable (c1: 1, c40: 2, c80: 4, c120: 4)".to_string());
1108+
env_info.push("- **pgbench jobs**: variable (c1: 1, c40: 4, c120: 4, c500: 4, c10k: 4)".to_string());
10991109
}
11001110

11011111
// Record end time

tests/bdd/world.rs

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -142,58 +142,62 @@ impl DoormanWorld {
142142
let doorman_workers = std::env::var("BENCH_DOORMAN_WORKERS")
143143
.ok()
144144
.filter(|s| !s.is_empty())
145-
.unwrap_or_else(|| "4".to_string());
145+
.unwrap_or_else(|| "12".to_string());
146146
result = result.replace("${DOORMAN_WORKERS}", &doorman_workers);
147147

148148
let odyssey_workers = std::env::var("BENCH_ODYSSEY_WORKERS")
149149
.ok()
150150
.filter(|s| !s.is_empty())
151-
.unwrap_or_else(|| "4".to_string());
151+
.unwrap_or_else(|| "12".to_string());
152152
result = result.replace("${ODYSSEY_WORKERS}", &odyssey_workers);
153153

154154
// pgbench jobs can be overridden globally or specifically for client counts
155155
let global_pgbench_jobs = std::env::var("BENCH_PGBENCH_JOBS")
156156
.ok()
157157
.filter(|s| !s.is_empty());
158158

159-
let pgbench_jobs_c1 = global_pgbench_jobs
159+
// C1 always needs 1 thread to avoid pgbench error: "number of clients (1) must be a multiple of number of threads"
160+
let pgbench_jobs_c1 = "1".to_string();
161+
result = result.replace("${PGBENCH_JOBS_C1}", &pgbench_jobs_c1);
162+
163+
let pgbench_jobs_c40 = global_pgbench_jobs
160164
.clone()
161165
.or_else(|| {
162-
std::env::var("BENCH_PGBENCH_JOBS_C1")
166+
std::env::var("BENCH_PGBENCH_JOBS_C40")
163167
.ok()
164168
.filter(|s| !s.is_empty())
165169
})
166-
.unwrap_or_else(|| "1".to_string());
167-
result = result.replace("${PGBENCH_JOBS_C1}", &pgbench_jobs_c1);
170+
.unwrap_or_else(|| "4".to_string());
171+
result = result.replace("${PGBENCH_JOBS_C40}", &pgbench_jobs_c40);
168172

169-
let pgbench_jobs_c40 = global_pgbench_jobs
173+
let pgbench_jobs_c120 = global_pgbench_jobs
170174
.clone()
171175
.or_else(|| {
172-
std::env::var("BENCH_PGBENCH_JOBS_C40")
176+
std::env::var("BENCH_PGBENCH_JOBS_C120")
173177
.ok()
174178
.filter(|s| !s.is_empty())
175179
})
176-
.unwrap_or_else(|| "2".to_string());
177-
result = result.replace("${PGBENCH_JOBS_C40}", &pgbench_jobs_c40);
180+
.unwrap_or_else(|| "4".to_string());
181+
result = result.replace("${PGBENCH_JOBS_C120}", &pgbench_jobs_c120);
178182

179-
let pgbench_jobs_c80 = global_pgbench_jobs
183+
let pgbench_jobs_c500 = global_pgbench_jobs
180184
.clone()
181185
.or_else(|| {
182-
std::env::var("BENCH_PGBENCH_JOBS_C80")
186+
std::env::var("BENCH_PGBENCH_JOBS_C500")
183187
.ok()
184188
.filter(|s| !s.is_empty())
185189
})
186190
.unwrap_or_else(|| "4".to_string());
187-
result = result.replace("${PGBENCH_JOBS_C80}", &pgbench_jobs_c80);
191+
result = result.replace("${PGBENCH_JOBS_C500}", &pgbench_jobs_c500);
188192

189-
let pgbench_jobs_c120 = global_pgbench_jobs
193+
let pgbench_jobs_c10000 = global_pgbench_jobs
190194
.or_else(|| {
191-
std::env::var("BENCH_PGBENCH_JOBS_C120")
195+
std::env::var("BENCH_PGBENCH_JOBS_C10000")
192196
.ok()
193197
.filter(|s| !s.is_empty())
194198
})
195199
.unwrap_or_else(|| "4".to_string());
196-
result = result.replace("${PGBENCH_JOBS_C120}", &pgbench_jobs_c120);
200+
result = result.replace("${PGBENCH_JOBS_C10000}", &pgbench_jobs_c10000);
197201

198202
result
199203
}

0 commit comments

Comments
 (0)