HeliosProxy v0.3.1: Faster Pool Checkout, Lock-Free Cache Reads
HeliosProxy v0.3.1 is a patch release focused on the request path. Single-threaded pool checkout is 28–37% faster than v0.3.0 on the same machine. Reading the pool metrics snapshot is 3.1× faster. PostgreSQL wire parsing no longer allocates per prepared-statement parameter. L1 cache hits take a read lock only, so concurrent requests to the same key no longer serialise. No API changes — all 24 feature modules, feature flags, CLI, admin API, and Docker image surface are unchanged.
What Changed
Four hot-path improvements, none of which alter observable behaviour beyond latency:
1. Connection-Pool Checkout
Per-node connection checkout no longer serialises through a global lock. Idle-connection pop and semaphore acquire now happen outside the per-pool map lock; per-node state is held behind a cheap cloneable Arc<Semaphore>. The net effect on the acquire_release microbenchmark:
cargo bench --bench pooling -- --quick
(same machine, single-threaded, criterion quick mode)
pool/acquire_release/single 471.81 ns -> 329.83 ns (-30%)
pool/throughput/sequential_acquire/1 490.36 ns -> 353.88 ns (-28%)
pool/throughput/sequential_acquire/10 5.354 us -> 3.351 us (-37%)
pool/throughput/sequential_acquire/50 25.36 us -> 16.95 us (-33%)
These numbers are the cost of the pool primitive itself, measured in isolation. A real proxy request path also pays for TLS, authentication, query routing, caching, and the backend round trip — this change reduces one component, not the total. We have not benchmarked mixed-workload or multi-threaded deployments under v0.3.1; doing so responsibly requires a separate test harness that we are not yet publishing numbers from.
2. Lock-Free Pool Metrics
Pool counters — acquires, timeouts, connections created, connections closed, recycles, validation failures — are now AtomicU64 loads. Reading the full pool.metrics() snapshot no longer contends with checkouts:
pool/metrics/read_metrics 35.90 ns -> 11.51 ns (-68%, 3.1x)
If you scrape metrics at 1 Hz this is invisible. If you hook the proxy into a per-request telemetry path that reads counters inline, the change is material.
3. Zero-Allocation PostgreSQL Parse
Prepared-statement parameter values arriving in a Bind message are now handled as reference-counted slices into the original protocol buffer, rather than heap-allocated Vec<u8> copies. C-string reads in the parser use a single scan to locate the terminator instead of incremental buffer growth. No benchmark number is quoted for this yet — there is no dedicated parser bench. The change is visible in the regression tests:
src/protocol.rs::tests::test_read_cstring_unterminated
src/protocol.rs::tests::test_bind_message_param_values_are_bytes
If you would like a real throughput figure for protocol parsing, that requires a bench we have not yet authored. We are noting the change here because it is visible in the code and covered by tests, but we are not putting a speedup multiplier on it.
4. Concurrent L1 Cache Hits
L1 is the per-connection hot cache, configured under [cache.l1]. Before v0.3.1, a cache hit acquired a write-like guard to update the access counter; threads hitting the same key serialised. In v0.3.1, the guard is a read lock and the counter is an atomic, so many threads can hit the same cached query in parallel:
src/cache/l1_hot.rs::tests::test_concurrent_hits_read_lock_only
(16 threads x 500 iterations against the same key)
No single-query latency number is quoted — L1 hits were already sub-microsecond. The improvement shows up as headroom under heavy read-skew, not as a smaller p50.
What We Are Not Claiming
We want to be explicit about the boundaries of this release's benchmark claims:
- Not "2–4× faster." The pool benches above are 28–37% on single-threaded checkout. Multi-threaded and mixed-workload numbers will arrive when we have a bench that measures them honestly.
- Not a PgBouncer comparison. The earlier v0.1.0 PgBouncer comparison bench has not been re-run on v0.3.1 hardware. Do not read a v0.3.1 improvement as a v0.3.1-vs-PgBouncer improvement.
- No throughput figure for protocol parsing or L1 cache. No dedicated bench exists. Claims about those two items are qualitative plus the regression tests linked above.
Under-the-Hood Correctness Fixes
Two reliability fixes ship with the release. They are listed in the CHANGELOG and we mention them here for completeness:
max_connectionsis now enforced against in-use connections. A semaphore permit leak that allowed the pool to overshoot the configured cap has been fixed. Regression test:test_max_connections_enforced_while_in_use.- Unterminated C-strings in protocol input now return a protocol error instead of silently consuming the buffer. Regression test:
test_read_cstring_unterminated.
Upgrading
v0.3.1 is a drop-in replacement for v0.3.0. No config changes are required. Feature flags, the module list, the CLI, the admin API, and the Docker image surface are unchanged.
# Docker
docker pull ghcr.io/dimensigon/hdb-heliosdb-proxy:0.3.1
docker compose up -d
# Cargo (if building from source)
cargo install heliosproxy --version 0.3.1
What's Next
The immediate follow-ups we have in view:
- A multi-threaded pool bench so we can publish contended-checkout numbers alongside the single-threaded ones.
- A dedicated protocol-parse bench, so claims about the zero-allocation work can carry a real number rather than a code-level description.
- An updated PgBouncer comparison on current hardware with v0.3.1.
HeliosProxy runs against any PostgreSQL-wire-compatible backend — PostgreSQL 12+, HeliosDB Nano/Lite/Full, CockroachDB, YugabyteDB, TimescaleDB, Citus, AlloyDB. It is open source under Apache 2.0. The full CHANGELOG for v0.3.1 lives in the Proxy repository.