ADR-058: Pingora Integration β Embedded Connection Pool
- Status: π Superseded by ADR-073 (2026-10-25)
- Date: 2026-03-16
- Decision Makers: Christophe Aboulicam
- Related: ADR-024 (Gateway Modes), CAB-1847, CAB-1849
Contextβ
Cloudflare's Pingora (Rust, Apache 2.0) replaced nginx and handles 1+ trillion requests/day. STOA Gateway is built on axum + reqwest + tokio. The question: how should STOA leverage Pingora?
Why Pingoraβ
The Business Case (decisive factor)β
| Signal | axum-only | With Pingora |
|---|---|---|
| Enterprise pitch | "Custom Rust gateway" | "Built on Pingora β Cloudflare's proxy framework" |
| Competitive position | Same tier as custom proxies | Same tier as Cloudflare Workers, Fastly Compute |
| CISO checkbox | Unknown stack | Battle-tested at 1T+ req/day |
| OSS credibility | Generic framework | Infrastructure-grade pedigree |
| Market uniqueness | None | Only API gateway built on Pingora |
No API gateway vendor β Kong, Gravitee, Envoy, agentgateway β uses Pingora. This is a first-mover advantage in positioning.
The Technical Caseβ
| Feature | reqwest (current) | Pingora Connector |
|---|---|---|
| Connection pool | Per-client instance | Shared across all workers (Cloudflare's key win) |
| H2 multiplexing | Per-connection | Global stream multiplexing |
| Pool reuse at scale | Degrades >10K RPS | Designed for 1T+ req/day |
| Zero-copy proxy | No (userspace copy) | Kernel splice/sendfile (future) |
At under 1K RPS the difference is 0.5ms p95. At 50K+ RPS, shared pooling avoids connection exhaustion that per-client pools hit.
Options Evaluatedβ
| Option | Description | Effort | Verdict |
|---|---|---|---|
| A: Full migration | Replace axum with Pingora server | 55+ pts | Rejected β rewrites 400+ routes |
| B: Sidecar | Pingora front-proxy β stoa-gateway | 21 pts | Rejected β extra hop, thin value |
| C: Embedded | Pingora connector inside stoa-gateway | 13 pts | Accepted |
| D: Patterns only | Copy Pingora patterns, don't use crate | 0 pts | Superseded by C |
Why Embedded (Option C) Wonβ
- One binary β no sidecar, no extra hop, no deployment complexity
- Same marketing claim β "Built on Pingora" is equally true for embedded connector
- Genuine value β Pingora's shared pool is the actual Cloudflare advantage, not the server
- Feature-gated β
--features pingoraenables it; default build stays reqwest (no cmake dependency) - Incremental β proxy path migrates to PingoraPool gradually; MCP/admin/auth stay on axum untouched
Decisionβ
Embed pingora-core::connectors::http::Connector inside stoa-gateway behind a pingora feature flag.
Architectureβ
Client β stoa-gateway (:8080)
β
ββ MCP / admin / auth / OAuth β axum handlers (unchanged)
β
ββ Proxy routes β PingoraPool.send_request()
β
ββ pingora-core shared connection pool
β Backend (H1/H2, TLS, keepalive)
One binary. One port. One deployment. Pingora manages upstream connections; axum manages HTTP routing.
Implementationβ
| PR | What |
|---|---|
| #1799 | stoa-pingora/ standalone binary (sidecar POC β superseded) |
| #1801 | PingoraPool embedded in stoa-gateway (production path) |
| #1803 | CI/CD: FEATURES=kafka,pingora in Docker build |
Phase Compatibilityβ
Our ProxyPhase trait maps 1:1 to Pingora's ProxyHttp:
| STOA ProxyPhase | Pingora ProxyHttp | Status |
|---|---|---|
request_filter | request_filter | Match |
upstream_select | upstream_peer | Match |
upstream_request_filter | upstream_request_filter | Match |
response_filter | upstream_response_filter | Match |
logging | logging | Match |
error_handler | error_while_proxy | Match |
If a full Pingora migration is needed at v2.0, phase implementations port directly.
What STOA Has That Pingora Doesn'tβ
| Capability | STOA | Pingora |
|---|---|---|
| eBPF kernel-level HTTP parsing | XDP + TC programs | Not built-in |
| UAC policy enforcement at kernel level | BPF maps synced from gateway | Not built-in |
| WASM plugin runtime | wasmtime integration | Not built-in |
| MCP protocol (AI agent gateway) | Full SSE/WS/JSON-RPC | Not applicable |
| Multi-gateway federation | 7 adapter types | Not applicable |
STOA uses Pingora for what it does best (connection pooling) and adds layers Pingora doesn't offer.
Consequencesβ
- Production image built with
FEATURES=kafka,pingora(cmake required in Docker) PingoraPoolavailable for proxy path; reqwest remains as fallback- Default build (no features) still works without cmake dependency
- Marketing: "STOA Gateway β powered by Pingora's connection engine"
- NOTICE file includes Pingora Apache 2.0 attribution
- Re-evaluate full migration at v2.0 or 50K+ RPS