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_connections is 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.

← Back to Blog