Est.
FeaturesLong read

Why Your p99 Looks Fine in Staging and Breaks in Production

Staging can't replicate the shard contention and query patterns that create real tail latency.

· 10 min read · Updated
Cover illustration for “Why Your p99 Looks Fine in Staging and Breaks in Production”
Features · September 22, 2026 · 10 min read · 2,228 words

The gap between a staging dashboard that looks fine and a production system that breaks comes down to one thing: staging traffic simply cannot produce the query patterns, fan-out behavior, and resource contention that generate real tail latency. That gap is a structural mismatch between what staging tests and what production throws at a database, and closing it means understanding the mechanisms that cause it.

Start with what average latency hides. A single slow shard, a garbage collection pause, or a compaction job competing for disk I/O gets buried by the sheer volume of fast responses around it. But the user who hit that one bad request doesn't experience an average. They experience five seconds of a spinning widget, and that's the whole interaction as far as they're concerned.

This is why p99, the latency threshold below which most requests complete, matters more than any blended number. At meaningful query volume, that 1% is a real, countable group of frustrated people every single day.

What makes this worse is that intermittent slowness does more damage to trust than steady, moderate slowness. A user who hits a five-second freeze doesn't think "the system is a bit slow today." They think the data is stale, or the product is broken, and they go find a spreadsheet to do the job manually. For any product that lives or dies on contractual service-level agreements, fraud detection, ad-tech bidding, anything billed as real-time, a low average sitting next to a high p99 is a breach waiting to happen, no matter how clean the median looks on the monthly report.

Scatter-gather fan-out turning a rare per-node event into a near-certain user experience

The reason tail latency behaves so differently in distributed OLAP systems than in simpler architectures comes down to how a single query actually gets answered. In a scatter-gather design, a query doesn't run once. The response time users feel is the maximum of all those shard times.

That single fact changes the entire math of reliability. A node that's slow only 1% of the time sounds like a non-issue in isolation. But the chance that a user's query touches at least one slow shard rises fast as shard count grows. The formula is straightforward: the probability of hitting at least one slow node equals 1 minus (1 minus P) raised to the power of N, where P is the per-node slowness rate and N is the shard count. Run that out at a high enough shard count: a "rare" slow-node event ends up touching nearly two out of every three user queries.

Think about what that means in practice. Adding more shards to get more parallelism doesn't just add more throughput. It also adds more opportunities for one of them to be slow on any given query, so scaling hardware without addressing tail behavior can make the user-visible tail worse, not better.

Staging almost never runs with production shard counts, so this compounding effect, the one actually driving production p99, never appears in pre-release testing.

There's a second multiplier working alongside shard count: how busy the system is. Queuing theory shows that tail latency grows sharply as utilization approaches capacity, not gradually. A system at high utilization behaves nothing like the same system at low utilization, and staging environments almost never get anywhere close to production load. So even setting fan-out aside, staging's tail is artificially calm just because the system isn't working hard enough to reveal how it behaves under strain.

Diagram: How Shard Count Turns a Rare Slow Node into a Common User Experience. Visualizes: Visualize the fan-out probability formula: the chance a user hits at least one slow shard equals 1 minus (1 minus P) raised to the power of N, where P is the…

The specific OLAP internals that produce tail spikes staging never triggers

Tail latency in a columnar OLAP engine doesn't come from nowhere. It traces back to specific, nameable things happening inside the storage and execution layers, and each one needs production conditions, real data volume, real concurrency, real cardinality, to actually fire. Staging under-represents all three, and the same bugs hide in pre-release testing and detonate in production.

Compaction is the clearest example. Background merge jobs compete with live queries for CPU and disk I/O, and when production write rates hit, that contention runs constantly. The tail spike this produces in production simply doesn't exist in staging because the two events never collide there.

Data skew works the same way. Columnar engines use zone maps, bloom filters, and column statistics so they can skip over data a query doesn't need. That pruning machinery depends entirely on the shape of real data. When production cardinality creates a hot partition, one customer or one date range that gets hit far more than others, the pruning stops helping and p99 and p50 pull apart sharply. Synthetic staging data gets built to look clean and evenly distributed, so the pruning looks like it's working, until a production dataset with real skew breaks it.

Query shape matters just as much as data shape. A SELECT * or a loosely scoped column list forces the engine to read every column instead of just the ones a query needs. Production users don't share that knowledge, and the wide-read path they trigger by accident never gets exercised before release.

Schema design sits underneath all of this. Denormalized schemas trade extra storage for fewer joins, but that trade only works if zone-map pruning actually works against the data it's built for. A staging schema loaded with anything other than production-representative distributions will show healthy, efficient pruning right up until real cardinality breaks the assumption it was built on.

Then there's storage itself. When a query needs to reach cold data sitting in object storage, GET latency behaves very differently under sustained, high-parallelism production scan volume than it does under the occasional test query staging sends its way. Staging rarely puts enough simultaneous pressure on that storage layer to see how it degrades, so that failure mode stays invisible until real traffic arrives.

Staging traffic distributions that are structurally wrong, not just smaller

None of this is a volume problem that more staging load would fix. Staging traffic is built by people who already know the schema, so it systematically avoids the exact conditions that generate real tail latency.

Think about who writes staging test queries. They're written by engineers who know where the data lives, so those queries land in dense, well-indexed regions almost by habit. That gives a false tail in places that don't matter in production and hides the real tail sitting in sparse or skewed regions nobody thought to test.

Real users don't share that knowledge, and that's exactly the point. A scripted load test only does what it was told to do, so it can't reproduce that.

The Kafka example from Allegro makes the pattern concrete. A single-digit millisecond median sat next to a one-second p99 and a p999 that reached three seconds. An infrastructure fix, migrating to XFS, cut the tail dramatically, yet the average barely moved. That's the tell: if fixing the tail doesn't touch the median, the tail was never a more intense version of the same workload. It's a separate mechanism entirely, and tools built to watch the median will miss it every time.

Measurement habits make this worse. Load generators that wait for one response before sending the next naturally slow down the moment the system slows down, so they never build up the queue depth that actually produces the worst tail events. The test politely adapts to the system instead of stressing it. Measuring from server-side logs compounds the same blind spot: server timing starts only once a request is accepted, and queueing time before that point is exactly where tail events accumulate under real concurrency.

Agentic query patterns as the newest and least-characterized source of production tail spikes

A newer source of tail latency has nothing to do with human dashboard users. AI agents querying analytical databases produce a query distribution unlike anything a dashboard generates, and almost no staging environment runs agents at production concurrency yet, making this the gap most likely to catch teams off guard through 2026.

The difference starts with how an agent approaches a database. A human dashboard issues pre-written, already-optimized SQL against tables someone already understands. An agent does the opposite: it probes the schema first, examines structure, samples data, and only then writes a multi-step query shaped by whatever that inspection turned up. The query plan is discovered live, not written in advance.

The Model Context Protocol is part of what makes this possible, letting agents inspect database structure as a first step before writing anything. That means an entirely new class of exploratory, schema-inspection queries now precedes every analytical query an agent runs. Those queries didn't exist in production logs before agent adoption, so even teams careful enough to test with real historical traffic have nothing to replay in staging.

Chaining multiplies the volume of queries the system has to handle. A typical agent workflow runs a retrieval step to figure out which tables matter, a schema-fetching step to pull definitions and sample data, and only then the actual analytical query. Each of those is a separate database round trip. One user question can turn into several, sometimes dozens, of individual queries, multiplying the load per action in a way no staging script accounts for.

An agent in production firing hundreds of these chained queries produces a mix that looks nothing like a scripted staging test. The tail of that distribution appears as long scans the agent only discovers it needs after probing the schema, and those register in monitoring as sudden, severe p99 spikes with no obvious trigger. Pricing adds a second layer of pain here: per-query billing models penalize an agent every time it asks a question, so a curious, exploratory agent session is both slower and more expensive than a traditional dashboard load ever was.

If the agent treats that text as a command rather than data, it can be redirected to pull information it shouldn't or call tools with arguments an attacker chose. That risk only exists once agents are running against real, live production data, which is one more reason agent access needs real architectural boundaries rather than open permissions.

Production-grade agentic database access requirements staging never tests

Giving an agent production database access without isolation, logging, or a sandbox to work in doesn't just open a security hole. It makes it close to impossible to trace a p99 incident back to the agent session that caused it.

Start with compute isolation. Isolated, read-only compute for agent workloads, kept separate from the resources serving live dashboards, gives operators something to diagnose and something to limit, instead of one shared pool where cause and effect blur together.

Logging matters just as much as isolation. Full logging for every agent interaction, not sampled, not summarized, is what turns an unexplained spike into a traceable one.

Sandboxed branches solve a different piece of the puzzle: letting agents work against real production data distributions, the actual cardinality and skew that staging can't fake, without touching live production state. That gives pre-production testing something it's never had before: exposure to the exact query shapes that generate real tail latency, tested safely before an agent ever touches the system users depend on.

Structured commands for schema inspection, validation, and deploying pipelines as code, instead of letting an agent issue arbitrary SQL, narrow what an agent is even capable of asking for.

Instrumenting staging so it exercises the tail

Closing this gap means building a per-stage latency budget rather than a blended end-to-end average, changing what gets measured, how load gets generated, and what data the system runs against. Fixing only one of these leaves the rest of the mismatch fully intact.

Measurement has to move to the client. Server-side logs start the clock only once a request is accepted, missing the queueing time that builds up before that point, which is exactly where tail events accumulate under real concurrency. Instrumentation needs to sit wherever it can see the full elapsed time a user actually experiences, start to finish.

Load generation needs to stop waiting politely for responses. Coordinated omission, where a test tool sends its next request only after the last one returns, means the test slows down exactly when the system slows down, and the queue depth that produces the worst tail events never builds. Open-loop load generators that fire requests on a fixed schedule regardless of what's still pending are what actually recreate production contention.

Latency budgets should be built per stage. Instrumenting embedding, retrieval, reranking, and execution separately at p50, p95, and p99, rather than relying on a blended end-to-end average, is what moves SLO compliance from poor to acceptable in production RAG systems. The same discipline applies directly to OLAP pipelines: a single end-to-end average hides which stage is actually overspending its time budget.

Utilization needs to match production too. Tail latency at high utilization isn't a straight-line extrapolation from tail latency at low utilization. The relationship bends sharply as a system approaches its capacity, so staging has to run at production-level load to see the tail behavior that actually matters.

Running staging with concurrent write workloads at production rates, so compaction events actually overlap with query execution, is the only way to reproduce the I/O and CPU contention that drives compaction-related tail spikes.

A release that improves median latency while quietly widening p99 is still a regression, and the only way to catch it before production is to set the gate on the tail metric directly.

Sources

  1. Tackling P99 Latency: Boost User Experience
  2. A Latency Budget for Production RAG

More in Features