I spent over five years on the Vault UI on-prem team. From day one until my last day, one request came up again and again: API pagination.
The answer was usually some mix of “it’s complicated” or “we can't because of the storage layer”. Having since moved into Customer Engineering and dug deep into the infamous storage layer, I get it — it really is complicated.
But the reality is simpler: browsers don’t do well rendering massive payloads. The UI team tried to simulate pagination using Ember Data caching and incremental rendering, but under the hood the API still returned everything. As a result, users often complained about slow lists or even broken pages.
So let’s put numbers to what we already know: the API can handle scale, but the UI struggles when lists get large.
And yes, I’ve heard pagination is in progress. So maybe this is a moot point, but consider it proof in numbers that this is a real issue.
Do the API the UI handle serving up thousands of items with the same performance?
vault-benchmark
for backend load, Chrome HAR captures for UI latency.
The backend performed exactly as you’d hope.
Throughput matchd the target requests per second all the way up to 1500.
No “knee” point was observed at these loads. Writes are slightly slower but follow a consistent curve.
The UI told a different story.
The backend was fine, but the UI introduced tail stalls as lists got larger. The user perception of “slowness” came from the UI, not the API.
The p99 spikes were concentrated around list views. Reads, writes, and deletes remained low-latency — in fact, they often looked better at higher RPS.
list
endpoint returns all keys under a path. The result: the backend scales, but the UI struggles.
Add pagination flags to the API’s list
endpoint. Even basic chunking would let the UI keep pace and avoid rendering bottlenecks.
1. Vault’s backend scales linearly to 1500+ RPS with sub-2 ms p95 latency — no knee point observed.
2. The UI median stays fine (~2 ms), but p95/p99 latency spikes 5–10× higher, up to 30–40 ms.
3. This happens because even though the UI renders in batches of 100, it still:
- downloads the entire payload,
- blocks on a single JSON.parse
,
- normalizes every record into the store,
- builds indexes/search structures on the full set, and
- triggers GC pauses from large allocations.
4. Those heavy bursts show up as tail stalls (p95/p99), creating the perception of slowness.
5. The real fix is server-side pagination (and keys-only list responses), so the UI only works with what it needs up front instead of chewing through thousands of records at once.