PostgreSQL query infrastructure

A PostgreSQL pooler that shows its evidence

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.

  • PostgreSQL 13 to 19
  • Transaction, session and statement pooling

What StreamQL does today

Every item below is implemented and tested against real PostgreSQL. Items that are not built yet are listed separately under roadmap.

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.

Available

Worker fleet

A 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.

Available

Load balancing exports

Publish 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.

Available

Operator console

Pools, 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.

Available

Slow-query insights

Per-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.

Available

Local slow-risk estimate

An 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, research

Decisions and trace evidence

With 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.

Available

MCP access for AI agents

Read-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.

Available

Access, alerts and secrets

OIDC 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.

Available

Deploy where you run

A 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.

Available

Limits that hold

Per-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.

Available

Change without downtime

Hot 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.

Available

Follow one query through the pooler

StreamQL 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.

    How it works

    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.

    Clients

    ApplicationsAny PostgreSQL driver
    psql, pgbenchScripts and tools
    AI agentsOver MCP, read-only

    Balancing

    Load balancerlibpq multi-host, HAProxy, nginx, DNS or the StreamQL TCP balancer

    Workers

    Worker APools, evidence, insights
    Worker BPools, evidence, insights
    Worker CPools, evidence, insights

    Database

    PostgreSQL13 to 19, self-managed or a managed service

    Primary control plane

    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.

    One node

    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.

    clients ──► streamql ──► PostgreSQL
    Start here

    A fleet

    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.

    clients ──► balancer ──► workers ──► PostgreSQL primary ◄── join, config, budget
    Available Primary HA coming

    Every decision has a reason code

    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.

    Statements
    Decision recordForwarded

      Illustrative records written in the style of the console. Reason codes shown here are real StreamQL codes.

      A tour of the operator console

      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.

      Find slow queries, then find out why

      Insights samples statements without blocking the data path and counts what it drops. It captures plans with plain EXPLAIN and never ANALYZE.

      • Quantiles per template with 2% relative error, kept for 8 days.
      • A same-hour 7-day baseline and a regression test, not a fixed threshold.
      • Hints for missing, unused and duplicate indexes, stale statistics, bloat, lock contention, long transactions and N+1 patterns.
      • pg_stat_statements deltas and wait-event sampling on PostgreSQL 13 to 19.
      Optional, off by default

      Slow-risk estimate, honestly

      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.

      A hint comes with its statement

      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));

      Performance, measured and bounded

      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.

      10 to 11 of 12

      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.

      5 to 16% behind

      Two PgBouncer processes (SO_REUSEPORT) are ahead in most cells. TPC-B with prepared statements is a tie.

      4.1 KiB

      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.

      Throughput in transactions per second, simple protocol, 2 CPUs per pooler
      WorkloadStreamQL, tokio (default)StreamQL, reactor (opt-in)PgBouncerPgBouncer, 2 processesPgDogOdyssey
      Select-only, 10 clients85,46686,44592,41489,42981,08678,187
      Select-only, 100 clients143,782144,962103,487151,461122,19434,805
      Select-only, 500 clients140,995142,201105,251151,025127,28043,257
      TPC-B, 10 clients12,80212,94313,10413,73812,22513,894
      TPC-B, 100 clients19,89119,82815,35621,04917,53516,527
      TPC-B, 500 clients16,95317,56616,72918,55716,29016,893

      Where StreamQL is behind

      • At 10 clients nothing is saturated and per-transaction latency decides. StreamQL is 3 to 11% behind the best pooler in most 10-client cells.
      • Per CPU, StreamQL spends 20 to 40% more than single-process PgBouncer in simple and extended select, and its p99 is higher than the best competitor in nearly every 2-CPU cell.
      • At 1 CPU, extended select at 100 and 500 clients is 23% and 32% behind PgBouncer. At 4 CPUs every cell is lost or tied, with PostgreSQL and pgbench as the ceiling.
      • Peak memory under load on the reactor engine is 83 to 131 MiB at 100 to 500 active clients, against 17 to 35 MiB on tokio.

      Memory per client, idle

      Resident memory with idle clients
      PoolerIdle MiB1,000 clients10,000 clientsKiB per client
      PgBouncer8.29.318.31.1 / 1.1
      PgDog3.945.3404.942.4 / 41.0
      Odyssey33.461.3258.628.6 / 23.0
      StreamQL, tokio (default)10.721.0101.410.5 / 9.2
      StreamQL, reactor (opt-in)12.925.153.312.5 / 4.1

      Pool-only builds. KiB per client at 1,000 and 10,000 clients.

      Quick start

      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.

      deploy/docker — zsh
      1. Clone the repository

        The Compose file lives in deploy/docker.

        git clone https://github.com/streamql/streamql
        cd streamql/deploy/docker
      2. Add secrets

        The 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_token
      3. Start StreamQL

        Clients connect on port 6432 and the console is on port 9930.

        docker compose up -d
      4. Connect and open the console

        Use 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:9930

      The 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.

      Download

      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.

      No release published yet

      There is no packaged release to download yet. Build StreamQL from source, or watch the releases page.

      shell
      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 only

      Kubernetes

      kubectl apply -k deploy/k8s
      # or
      helm install streamql deploy/helm/streamql --namespace streamql --create-namespace

      Run these from a clone of the repository. See the quick start for the required values, and the installation guide for both editions.

      Compatibility

      The integration matrix runs every wire test against each major PostgreSQL version. Features that differ between versions are gated by version.

      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19

      Clients

      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.

      Platforms

      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.

      Managed PostgreSQL

      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.

      On the roadmap

      • LISTEN multiplexing and replica routing.
      • Primary high availability and fleet state in PostgreSQL.
      • Governor, admission control and result caching with consistency receipts.
      • Proxy wiring for eligibility analysis and the authorization gate.
      • BOLT builds and allocation profiling.

      In progress

      • Workload X-ray: labelled database time by template, with recommendations. Available as a crate and command-line tool.
      • Global connection budget enforced on every pool.
      • Consistency kernel for multi-member coherence.

      Questions

      Is StreamQL production ready?

      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.

      How is it different from PgBouncer?

      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.

      Does it cache query results?

      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.

      Does anything leave my network?

      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.

      Can AI agents query my database?

      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.

      Which pooling mode should I use?

      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.

      What happens during a restart or upgrade?

      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.

      Run it next to your pooler

      Start one node, open the console and read the evidence for yourself.

      Enterprise support

      For teams running StreamQL in a company.

      • Support from the team behind StreamQL.
      • Deployment planning and rollout of pools, clusters, workers and load balancing.
      • Cloud infrastructure, managed setup on your cloud.