Reusing catalog connections helps misses and writes, with a warm-read warning
The shared catalog was opening a new Valkey connection whenever a request loaded its metadata snapshot. We changed it to reuse a persistent multiplexed connection. A failed operation is still returned as a failure: reconnecting later must not silently replay a write whose outcome is uncertain.
In a new paired experiment, gateway cold-query and write/read medians generally improved by 12–17%. The exception was eight-client cold queries with cache serving off, which improved only 1.4%. Warm performance was mixed, and one important case regressed. This change reduces connection work; it does not establish an across-the-board latency improvement.
Compare within this experiment
We measured the original revision
187944e
against connection-reuse change
49da1cb,
built from validated source
c420872.
Each profile used old, new, new, old order, retaining native controls and all
trials. Cache coordination stayed enabled; serving off/on were separate profiles.
Native and old-gateway absolute latency were higher than in the earlier shared-cache experiment. We therefore keep these experiments separate. Their differences cannot identify a cause, and pooling their medians would be misleading.
Values below are gateway median trial p50 milliseconds. Cold means unused response-cache keys on loaded data. Write rows measure a write and its following read with one client.
| Workload | Serving off: old → new | Serving on: old → new |
|---|---|---|
| Cold sorted filter, one client | 2.590 → 2.159 | 3.155 → 2.788 |
| Cold sorted filter, eight clients | 7.761 → 7.650 | 10.692 → 9.344 |
| Write then point read | 3.524 → 2.914 | 4.008 → 3.392 |
| Write then filter counts | 4.673 → 3.974 | 5.137 → 4.481 |
Median improvements do not describe the whole result. Eight-client cold p95
increased from 64.454 to 71.301 ms with serving off and 51.765 to 57.449 ms
with serving on. The new enabled gateway’s cold medians remained approximately
4.1 and 4.8 times the faster native engine at one and eight clients. Native
engines also remained faster for write/point-read cycles. The gateway led the
write/filter-count comparison, whose identical refresh=true requests require
immediate search visibility; point-write cycles omit refresh on every engine.
Warm reads were not uniformly preserved
These are the separate cache-enabled warm rounds. Each cell shows one client / eight clients, in milliseconds.
| Gateway operation | Old | New |
|---|---|---|
| Read one document | 0.380 / 0.845 | 0.381 / 0.985 |
| Filtered, sorted page | 0.593 / 1.324 | 0.571 / 1.403 |
| Page after a sort cursor | 0.603 / 1.344 | 0.462 / 1.411 |
| Keyword terms buckets | 0.417 / 1.351 | 0.413 / 1.136 |
| Two filter counts | 0.403 / 1.037 | 0.403 / 1.046 |
Eight-client point-read p50 rose 16.5%, and p95 rose 27.5%, from 1.242 to 1.583 ms. The native point-read medians also increased roughly 12% between the corresponding old/new rounds. That drift prevents attributing the gateway change to connection reuse alone, but it does not make the adverse result disappear. Warm-read neutrality remains unproven.
The full summary includes all native, old-gateway and new-gateway rows, with p95 and throughput. The new gateway still led the warm search medians in this experiment; native point reads and the cold/write costs prevent a blanket engine-performance claim.
Replay the change
The corpus remained 10,000 documents, with identical client requests and v3 correctness assertions. Elasticsearch 8.15.0 and OpenSearch 3.9.0 each retained two CPUs and 4 GiB. Gateway, Qdrant 1.19.2 and durable Valkey shared that same aggregate budget. No builds ran during timing.
Build both pinned revisions before measuring and prepare the disposable shared
cache deployment using the
pinned harness instructions.
The retained round driver
and image override
belong in that harness directory. They replace only the gateway image, wait for
readiness, preserve volumes and refuse to overwrite evidence. Defaults verify the
recorded image IDs before changing the container. For a rebuilt image, pass its
explicit --old-image-id or --new-image-id and retain the resulting provenance.
Build the old revision as qdrant-es-gateway:cache-runtime and the new revision
as qdrant-es-gateway:catalog-transport. Fresh local builds generally have different
image IDs from the retained lab images. After verifying their source revisions,
record those local IDs and pass them explicitly:
old_image_id=$(docker image inspect qdrant-es-gateway:cache-runtime | python3 -c 'import json,sys; print(json.load(sys.stdin)[0]["Id"])')
new_image_id=$(docker image inspect qdrant-es-gateway:catalog-transport | python3 -c 'import json,sys; print(json.load(sys.stdin)[0]["Id"])')
python3 benchmark/shared-cache/transport_round.py off old 1 mixed \
--old-image-id "$old_image_id" --new-image-id "$new_image_id"
# Keep both ID arguments for each later call.
# Continue off: new 1, new 2, old 2; repeat the four rounds with on.
# Then run on old 1 warm, on new 1 warm, on new 2 warm, on old 2 warm.
All 129,600 measured HTTP responses passed checks across twenty raw rounds. The manifest records exact image IDs, revisions, every raw file and post-run observations. Memory snapshots are not peaks, and container-lifetime throttling counters cannot attribute individual latency changes. Valkey key-access counters are not response-cache hit rates; the retained observations do not provide a causal diagnosis. Published raw files add experiment/build labels to prevent accidental pooling; the manifest preserves their original hashes and identifies that metadata-only annotation. Connection reuse removes avoidable work from misses and writes; the warm-read regression and cold tails remain visible in the evidence.