Three pooling modes
Transaction, session and statement pooling. Prepared statements work across pooled backends, session state is replayed as a minimal diff, and sessions that must stay on one backend are pinned and counted.
AvailablePostgreSQL query infrastructure
StreamQL pools connections, scales out as a fleet of workers and records what it decided for every statement. Written in Rust, operated from a web console, deployed with Docker, Helm or an operator.
Every item below is implemented and tested against real PostgreSQL. Items that are not built yet are listed separately under roadmap.
Transaction, session and statement pooling. Prepared statements work across pooled backends, session state is replayed as a minimal diff, and sessions that must stay on one backend are pinned and counted.
AvailableA primary node is the control plane. Workers join with a one-time token, get their own TLS certificate and receive configuration generations with staged rollouts. Connection budgets are shared across members.
AvailablePublish workers as libpq multi-host strings, HAProxy or nginx configuration, DNS records or JSON, or use the StreamQL TCP balancer with weights, least connections, zone affinity and routing by database, user or tenant.
AvailablePools, gateways, decisions, trace, live and slow queries, alerts, users and roles, configuration with validate, diff and apply. Light, dark and high contrast, phone to desktop, 30 languages including right-to-left.
AvailablePer-template quantiles split into pool wait, database time and lock wait, a 7-day same-hour baseline, regression tests, captured plans and hints such as missing indexes, validated with HypoPG. Advice only, nothing is applied automatically.
AvailableAn optional model estimates the chance that an execution will be slow. It runs inside the StreamQL process with no external service, and it is off by default. Measured precision is high and recall is low, so it is labelled research.
Optional, researchWith evidence on, each statement leaves a record: category, reason code and the steps behind it. Open Decisions or Trace in the console to see why a statement was forwarded, pinned, refused or replayed.
AvailableRead-only access over MCP with per-token limits: role, tools, rows per call, statement timeout, calls per minute, database time per hour and an IP allowlist. Tokens are shown once and stored hashed, and every call is audited.
AvailableOIDC single sign-on, TOTP and WebAuthn multi-factor, break-glass accounts and roles. Alert channels for email, SMS, Slack, Telegram and signed webhooks. Secrets from Vault, Kubernetes, AWS, Azure and Google, with rotation.
AvailableA static, non-root OCI image, Docker Compose, Kubernetes manifests, a Helm chart, an operator with Gateway, Pool and Policy resources, OpenShift, EKS, AKS and GKE overlays, plus systemd units and deb and rpm packages.
AvailablePer-database and per-role connection limits, reserve pools, pool groups, per-tenant shares, queue deadlines that answer with error 53000, pre-authentication hardening and failed-login blocking.
AvailableHot reload with validate and diff, versioned configuration history, graceful drain on SIGTERM, and in-place binary upgrade by handing the listen socket to the new process.
AvailableStreamQL splits each statement's time into pool wait, database time and lock wait, so a slow query is blamed on the right layer. Pick a scenario.
Healthy pool ·
Illustrative timings. The slow statement scenario mirrors the layout of the Slow queries page; real numbers come from your own workload.
Clients connect with the PostgreSQL protocol to a balancer or directly to a worker. Workers multiplex many clients onto few backends. One primary node holds the configuration and the console.
Configuration. Generations are pushed to workers over mutual TLS, with staged waves, bake time and automatic pause when a worker rejects a config.
Budget. The primary hands each worker a share of the global connection budget so the sum never exceeds what PostgreSQL allows.
Console and API. Operators use the console, the admin API and Prometheus metrics. Workers appear with their drift, version and zone.
The default. A single process in front of PostgreSQL with pooling on, zero-config start from an upstream URL, the console and the admin API on the same node.
Add workers anywhere with streamql worker --join. Put a balancer in front, roll out configuration in waves and watch drift from one console. The primary itself is a single node for now.
StreamQL never silently changes what a statement does. It forwards, replays state, applies a policy or refuses, and records the category and a code in the Decisions and Trace screens. Choose a statement.
Illustrative records written in the style of the console. Reason codes shown here are real StreamQL codes.
Real screenshots of the console running against a local test node with live traffic. Each screen answers a question: what is running, what did it decide and what evidence backs that.
Swipe, scroll or use the arrow keys. Open a screen directly with a link such as #tour/slow-queries.
Insights samples statements without blocking the data path and counts what it drops. It captures plans with plain EXPLAIN and never ANALYZE.
pg_stat_statements deltas and wait-event sampling on PostgreSQL 13 to 19.A gradient-boosted model scores executions in-process, with no call to an outside service. On two shifted held-out workloads its precision was 0.92 and its recall 7 to 16 percent: when it raises a flag it is usually right, but it misses most slow executions, because many of them are short pool-saturation bursts that history cannot foresee. Treat it as a research feature.
Hints are advice only and nothing is applied automatically. A missing-index hint shows its evidence and a statement you can copy (sample data):
-- seq scan on 3.8 M rows, filter on tenant_id and lower(customer_name)
CREATE INDEX CONCURRENTLY orders_tenant_customer_lower_idx
ON orders (tenant_id, lower(customer_name));Third benchmark round: pgbench through each pooler in front of one PostgreSQL 18, same pooling settings, same CPU quota. StreamQL leads the single-process poolers at 100 and 500 clients and trails PgBouncer run as two processes. Both are shown below.
Cells at 100 and 500 clients where StreamQL beat PgBouncer, PgDog and Odyssey by more than the run-to-run spread (reactor 11, tokio 10). The rest are ties.
Two PgBouncer processes (SO_REUSEPORT) are ahead in most cells. TPC-B with prepared statements is a tie.
Idle memory per client at 10,000 clients on the reactor engine. PgBouncer uses 1.1 KiB, so PgBouncer is lower.
Transactions per second (TPS), higher is better, median of three rounds, 2 CPUs per pooler. Bold marks the best in a row and the bar is relative to it.
| Pooler | Idle MiB | 1,000 clients | 10,000 clients | KiB per client |
|---|---|---|---|---|
| PgBouncer | 8.2 | 9.3 | 18.3 | 1.1 / 1.1 |
| PgDog | 3.9 | 45.3 | 404.9 | 42.4 / 41.0 |
| Odyssey | 33.4 | 61.3 | 258.6 | 28.6 / 23.0 |
| StreamQL, tokio (default) | 10.7 | 21.0 | 101.4 | 10.5 / 9.2 |
| StreamQL, reactor (opt-in) | 12.9 | 25.1 | 53.3 | 12.5 / 4.1 |
Pool-only builds. KiB per client at 1,000 and 10,000 clients.
Point StreamQL at a PostgreSQL server you already have. No credentials are baked into the image or the file: secrets are read from files or a Kubernetes Secret.
The Compose file lives in deploy/docker.
git clone https://github.com/streamql/streamql
cd streamql/deploy/dockerThe upstream URL and the admin token are read from files, so nothing is stored in the image or the Compose file.
mkdir -p secrets
printf 'postgres://app:APP_PASSWORD@db.example.com:5432/app?sslmode=require' > secrets/upstream_url
openssl rand -hex 24 > secrets/admin_tokenClients connect on port 6432 and the console is on port 9930.
docker compose up -dUse the role and password from the upstream URL. The log shows the setup code that claims the console.
psql 'host=localhost port=6432 user=app dbname=app'
docker compose logs streamql | grep setup_code # then open http://localhost:9930The chart lives in deploy/helm/streamql.
git clone https://github.com/streamql/streamql
cd streamqlThis installs a single gateway. For production, use gateway.upstream.existingSecret and gateway.admin.existingSecret instead of values on the command line.
helm install streamql deploy/helm/streamql \
--namespace streamql --create-namespace \
--set gateway.upstream.url='postgres://app:APP_PASSWORD@db.example.com:5432/app' \
--set gateway.admin.token="$(openssl rand -hex 24)"Every setting is an environment variable named STREAMQL_<SECTION>__<KEY>. A value written as file:/path is read from that file. In Compose, put them under environment:.
STREAMQL_UPSTREAM__URL=file:/run/secrets/upstream_url
STREAMQL_ADMIN__TOKEN=file:/run/secrets/admin_token
STREAMQL_DEFAULTS__MODE=transaction # or session, statement
STREAMQL_DEFAULTS__MAX_SIZE=20 # server connections per database and role
STREAMQL_UPSTREAM__SSLROOTCERT=/etc/ssl/db-ca.pem # with sslmode=verify-full
STREAMQL_LOG__FORMAT=jsonThe same settings fit a TOML file. For example, to try the opt-in reactor engine:
[dataplane]
engine = "reactor" # default is "tokio"; plain-TCP upstreams only
evidence = true # one decision record per statement
[defaults]
mode = "transaction"
max_size = 20On the primary, open the console, go to Pools, then Workers, and create a join token.
Run this on any server that can reach the primary.
streamql worker --join https://primary.example.com:9931 --token JOIN_TOKENgit clone https://github.com/streamql/streamql
cd streamqlBuild the container image, the lean pool-only image without the console, or the binary.
docker build -t streamql .
make image-pool-only # pooling only, no console
cargo build --releaseThe image name in the Compose file and chart is ghcr.io/streamql/streamql. StreamQL is early software: run it next to your current pooler before you move traffic.
The latest release is read from GitHub in your browser. Nothing is sent to us. Pick an edition: full featured, or pool only without the web console.
There is no packaged release to download yet. Build StreamQL from source, or watch the releases page.
git clone https://github.com/streamql/streamql
cd streamql
cargo build --release -p streamql # full featured
cargo build --release -p streamql --no-default-features --features mimalloc # pool onlykubectl apply -k deploy/k8s
# or
helm install streamql deploy/helm/streamql --namespace streamql --create-namespaceRun these from a clone of the repository. See the quick start for the required values, and the installation guide for both editions.
The integration matrix runs every wire test against each major PostgreSQL version. Features that differ between versions are gated by version.
PostgreSQL protocol 3.0. Clients that ask for 3.2, including libpq 19, are negotiated down to 3.0. Tested with psql and pgbench, in simple, extended and prepared modes, with TLS and SCRAM-SHA-256, MD5 or trust. A per-driver compatibility table is on the roadmap.
Linux and macOS binaries, a static scratch-based OCI image, Docker, Kubernetes, OpenShift, Amazon EKS, Azure AKS and Google GKE, systemd and deb or rpm packages.
Capability probes and privilege tiers for Amazon RDS and Aurora, Azure Database for PostgreSQL and Google Cloud SQL and AlloyDB, with IAM token login in progress of being wired into pools.
It is early software. The pooler core, fleet, console and insights are implemented and tested against real PostgreSQL 13 to 19, but the project is young. The roadmap marks what is done, in progress and not started, and this page follows it.
StreamQL pools like PgBouncer and adds a console, a worker fleet with a control plane, slow-query insights and a decision record per statement. In the repository benchmarks StreamQL has higher throughput than single-process PgBouncer at 100 and 500 clients, but PgBouncer run as two processes is ahead, PgBouncer uses less CPU and memory per client, and its p99 is lower under load.
Not yet. Caching, coalescing and the consistency machinery behind them are on the roadmap and are not part of the current data path. Statements are forwarded unmodified.
No tracking and no external services are required. The slow-risk estimate runs inside the process. Alert channels and single sign-on call only the providers you configure.
Only if you create an MCP token. Each token has a role, a list of tools, row and time limits and an optional IP allowlist. SQL runs read-only, parameters are never returned and each call is audited.
Transaction pooling is the default and gives the most sharing. Use session pooling for clients that depend on session state, and statement pooling only when no transaction is open: it rejects BEGIN with error 0A000.
On SIGTERM a node stops being ready, stops accepting, lets open transactions finish and closes clients with 57P01. For in-place upgrades, the new binary takes over the listening socket.
Start one node, open the console and read the evidence for yourself.
For teams running StreamQL in a company.