|
1 | | -# Load Performance Testing for Web APIs Returning a Single PostgreSQL Function |
| 1 | +# Load Performance Testing for Web APIs Returning PostgreSQL Functions |
2 | 2 |
|
3 | | -This project performs load performance testing for Web APIs on different tech stacks that will execute a single [PostgreSQL function](https://github.com/vb-consulting/pg_function_load_tests/blob/master/src/postgres/init.sql) and return the result. |
| 3 | +This project performs load performance testing for Web APIs on different tech stacks that execute PostgreSQL functions and return the results. The goal is to **measure framework overhead**, not database performance - all test functions use `generate_series()` for constant, predictable database response times. |
4 | 4 |
|
5 | | -## Running Manually |
| 5 | +## Frameworks Tested |
6 | 6 |
|
7 | | -``` |
| 7 | +| Framework | Version | Port | Language | |
| 8 | +|-----------|---------|------|----------| |
| 9 | +| Django | 6.0.1 | 8000 | Python | |
| 10 | +| FastAPI | 0.128.0 | 8001 | Python | |
| 11 | +| Fastify | 5.7.1 | 3101 | Node.js | |
| 12 | +| Bun | 1.3.3 | 3104 | Bun/TypeScript | |
| 13 | +| Go (net/http) | 1.25 | 5200 | Go | |
| 14 | +| Spring Boot | 4.0.1 | 5400 | Java 24 | |
| 15 | +| Actix-web | 1.91.1 | 5300 | Rust | |
| 16 | +| Swoole | 6.0 | 3103 | PHP | |
| 17 | +| PostgREST | 14.3 | 3000 | Haskell | |
| 18 | +| .NET 9 Minimal API (EF) | 9.0 | 5002 | C# | |
| 19 | +| .NET 10 Minimal API (EF) | 10.0 | 5003 | C# | |
| 20 | +| .NET 10 Minimal API (Dapper) | 10.0 | 5004 | C# | |
| 21 | +| NpgsqlRest (AOT) | 3.4.5 | 5005 | C# | |
| 22 | +| NpgsqlRest (JIT) | 3.4.5 | 5006 | C# | |
| 23 | + |
| 24 | +## Benchmark Scenarios |
| 25 | + |
| 26 | +All scenarios use memory-only PostgreSQL functions (no table I/O) to isolate framework overhead: |
| 27 | + |
| 28 | +| Scenario | Endpoint | Tests | |
| 29 | +|----------|----------|-------| |
| 30 | +| **perf_test** | `GET /api/perf-test` | Comprehensive data type serialization (23 types) | |
| 31 | +| **perf_minimal** | `GET /api/perf-minimal` | Pure routing overhead baseline | |
| 32 | +| **perf_post** | `POST /api/perf-post` | JSON request body parsing | |
| 33 | +| **perf_nested** | `GET /api/perf-nested` | Nested JSON object serialization | |
| 34 | +| **perf_large_payload** | `GET /api/perf-large-payload` | Large response streaming/buffering | |
| 35 | +| **perf_many_params** | `GET /api/perf-many-params` | Query string parsing (20 parameters) | |
| 36 | + |
| 37 | +## Quick Start |
| 38 | + |
| 39 | +### Prerequisites |
| 40 | + |
| 41 | +- Docker and Docker Compose |
| 42 | +- Git |
| 43 | + |
| 44 | +### Clone and Build |
| 45 | + |
| 46 | +```bash |
8 | 47 | git clone https://github.com/vb-consulting/pg_function_load_tests.git |
9 | | -cd pg_function_load_tests |
10 | | -cd src |
| 48 | +cd pg_function_load_tests/src |
11 | 49 | docker-compose down && docker-compose up --build --detach |
12 | 50 | ``` |
13 | 51 |
|
14 | | -Please wait a few seconds for all services to initialize properly (health check isn't implemented yet), and then run from the same directory: |
| 52 | +Wait for health checks to pass (all services depend on PostgreSQL being ready). |
| 53 | + |
| 54 | +### Verify Services |
| 55 | + |
| 56 | +Test all 14 services and 84 endpoints: |
| 57 | + |
| 58 | +```bash |
| 59 | +./test-services.sh |
| 60 | +``` |
| 61 | + |
| 62 | +Or test against a remote server: |
15 | 63 |
|
| 64 | +```bash |
| 65 | +./test-services.sh your-server-ip |
16 | 66 | ``` |
17 | | -docker-compose exec test /bin/sh /scripts/run.sh |
| 67 | + |
| 68 | +## Running Benchmarks |
| 69 | + |
| 70 | +### Test Profiles |
| 71 | + |
| 72 | +The benchmark runner supports three profiles: |
| 73 | + |
| 74 | +| Profile | Purpose | Duration | Use Case | |
| 75 | +|---------|---------|----------|----------| |
| 76 | +| **minimal** | Markdown validation | ~2 min | Test output format before deploying | |
| 77 | +| **local** | Development testing | ~30 min | Validate changes locally | |
| 78 | +| **server** | Production benchmarks | ~4 hours | Full benchmark on dedicated server | |
| 79 | + |
| 80 | +### Local Testing |
| 81 | + |
| 82 | +**Quick markdown validation** (minimal profile): |
| 83 | +```bash |
| 84 | +docker-compose exec test /bin/sh -c "PROFILE=minimal /scripts/run-all.sh" |
18 | 85 | ``` |
19 | 86 |
|
20 | | -Keep it running on server: |
| 87 | +**Development testing** (local profile - default): |
| 88 | +```bash |
| 89 | +docker-compose exec test /bin/sh /scripts/run-all.sh |
21 | 90 | ``` |
22 | | -tmux # Start a new tmux session |
23 | | -# Run your command |
24 | | -docker-compose exec test /bin/sh /scripts/run.sh |
25 | | -# Press Ctrl+B, then D to detach |
| 91 | + |
| 92 | +**Run specific scenario only**: |
| 93 | +```bash |
| 94 | +docker-compose exec test /bin/sh -c "SCENARIO=minimal /scripts/run-all.sh" |
26 | 95 | ``` |
27 | 96 |
|
28 | | -Results will be saved to the `src/k6/results/` directory under the current timestamp subdirectory. |
| 97 | +Available scenarios: `all`, `perf-test`, `minimal`, `post`, `nested`, `large`, `params` |
| 98 | + |
| 99 | +### Server Testing |
| 100 | + |
| 101 | +For production benchmarks on a dedicated server (e.g., Hetzner CCX33): |
| 102 | + |
| 103 | +```bash |
| 104 | +# Start a tmux session (survives SSH disconnection) |
| 105 | +tmux |
| 106 | + |
| 107 | +# Run full benchmark suite |
| 108 | +docker-compose exec test /bin/sh -c "PROFILE=server /scripts/run-all.sh" |
| 109 | + |
| 110 | +# Detach: Press Ctrl+B, then D |
| 111 | +# Reattach later: tmux attach |
| 112 | +``` |
| 113 | + |
| 114 | +**Server profile settings:** |
| 115 | +- 60s test duration per combination |
| 116 | +- VUs: 1, 50, 100, 200 (up to 500 for minimal baseline) |
| 117 | +- 30s sleep between tests (TCP TIME_WAIT clearance) |
| 118 | +- JIT warmup phase before benchmarks |
| 119 | + |
| 120 | +### Results |
| 121 | + |
| 122 | +Results are saved to `src/_k6/results/<timestamp>/`: |
| 123 | +- `<timestamp>_all.md` - Unified summary with all results |
| 124 | +- Individual test summaries for each service/scenario combination |
| 125 | + |
| 126 | +## Architecture |
| 127 | + |
| 128 | +### PostgreSQL Configuration |
| 129 | + |
| 130 | +```yaml |
| 131 | +postgres: |
| 132 | + image: postgres:17.2-alpine |
| 133 | + command: postgres -c 'max_connections=2000' |
| 134 | +``` |
| 135 | +
|
| 136 | +**Connection calculation:** |
| 137 | +- Tests are serialized (one service at a time) |
| 138 | +- Active service: 100 connections (pool max) |
| 139 | +- 13 idle services: ~200 connections |
| 140 | +- Total during test: ~300 connections |
| 141 | +- Setting 2000 provides 6x headroom |
| 142 | +
|
| 143 | +### Test Isolation |
| 144 | +
|
| 145 | +Tests are **serialized** - only one service is tested at a time. This ensures: |
| 146 | +- No resource contention between services |
| 147 | +- Clean baseline for each test |
| 148 | +- Accurate framework overhead measurement |
| 149 | +
|
| 150 | +The 30s sleep between tests (server profile) allows: |
| 151 | +- TCP TIME_WAIT connections to clear |
| 152 | +- Connection pools to stabilize |
| 153 | +- CPU/memory to return to baseline |
| 154 | +- JIT-compiled code to cool down |
| 155 | +
|
| 156 | +## Manual Testing |
| 157 | +
|
| 158 | +Use `test.http` with VS Code REST Client or similar tools to manually test individual endpoints. |
| 159 | + |
| 160 | +Example: |
| 161 | +```http |
| 162 | +### Django - perf_test |
| 163 | +GET http://localhost:8000/api/perf-test?_records=1&_text=test&... |
| 164 | +
|
| 165 | +### PostgREST - perf_minimal |
| 166 | +GET http://localhost:3000/rpc/perf_minimal |
| 167 | +``` |
29 | 168 |
|
30 | 169 | ## Latest Results |
31 | 170 |
|
| 171 | +- [PostgreSQL REST API Benchmark 2025](https://npgsqlrest.github.io/blog/postgresql-rest-api-benchmark-2025.html) |
32 | 172 | - [Test Branch](https://github.com/vb-consulting/pg_function_load_tests/tree/202412302119) |
33 | 173 | - [Test Results Raw Output](https://github.com/vb-consulting/pg_function_load_tests/blob/202412302119/src/_k6/results/202412302119.md) |
34 | | -- [Parsed Tests Results Disucssion Thread](https://github.com/vb-consulting/pg_function_load_tests/discussions/8) |
| 174 | +- [Parsed Tests Results Discussion Thread](https://github.com/vb-consulting/pg_function_load_tests/discussions/8) |
35 | 175 | - [Interactive Chart](https://vb-consulting.github.io/blog/npgsqlrest/load-test/) |
36 | 176 |
|
37 | 177 | ## License |
38 | 178 |
|
39 | 179 | MIT |
40 | 180 |
|
41 | | -## 🤝 Contributing |
| 181 | +## Contributing |
42 | 182 |
|
43 | 183 | Contributions are welcome! Please feel free to submit a Pull Request. |
0 commit comments