definition

What p99 means

p99 is the 99th percentile of a latency distribution. It means: 99% of requests complete faster than this value, and 1% take longer. It is sometimes written as the "99th percentile latency" or "99p".

At 100 requests per second, p99 latency affects roughly 1 request per second. At 1,000 req/s, 10 requests per second hit or exceed the p99 threshold. These are not rare events — they're a continuous stream.

percentilemeaningat 1,000 req/s
p50Median — half of requests are slower, half faster500 req/s exceed this
p9595th percentile — 5% of requests are slower50 req/s exceed this
p9999th percentile — 1% of requests are slower10 req/s exceed this
p99999.9th percentile — 0.1% of requests are slower1 req/s exceeds this
finding your p99

Where to look in your APM tools

Most observability tools expose percentile latency. Here's where to find it in the common ones:

# Prometheus / Grafana — histogram_quantile
histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))

# Datadog — p99 latency metric
avg:trace.express.request.duration.by.resource_name{*}.as_rate().rollup(p99, 60)

# CloudWatch — ExtendedStatistics
{
  "ExtendedStatistics": ["p99"],
  "MetricName": "Duration"
}

# New Relic NRQL
SELECT percentile(duration, 99) FROM Transaction WHERE appName = 'myapp'
No APM? Use the presets

If you don't have real latency data yet, slowdep's built-in presets are calibrated from production observations for common services like 'postgres', 'redis', 'openai', and 'stripe'. Start there and refine when you have data.

using in slowdep

Configuring a custom profile from your data

Once you have your p50 and p99 from APM, plug them directly into slowdep's custom profile:

import { withLatency } from 'slowdep';

// Your APM shows: p50 = 12ms, p99 = 340ms, error rate ~0.3%
const slowQuery = withLatency(db.query, {
  p50: 12,
  p99: 340,
  errorRate: 0.003,
});

// Now your tests reflect actual production behaviour
const result = await slowQuery('SELECT ...');
timeout calibration

Why p99 determines your safe timeout value

Your request timeout must be larger than your p99, or you'll start rejecting legitimate requests. The usual rule of thumb is to set your timeout at roughly p999 — covering 99.9% of real responses while cutting off true hangs.

// Service: p50=5ms, p99=200ms, p999=2000ms

// Timeout too tight — rejects 1% of real requests
const TIMEOUT = 150;  // below p99, will fire on valid responses

// Timeout correct — only catches true hangs
const TIMEOUT = 2000; // at p999, covers 99.9% of legitimate responses

// Test the timeout fires when expected:
// mock Math.random to force a high-percentile sample
jest.spyOn(Math, 'random').mockReturnValue(0.999); // forces near-p99 sample
further reading

Related topics