Skip to main content
Every execution answers what was decided. Decision Replay answers what would change. Performance Profiling answers the third question: how fast is each rule, right now, on this version? LexQ measures latency continuously in production — no agent, no configuration, on every plan.

How measurement works

  • Deterministic sampling. Whether a call is sampled is a pure function of its traceId — anyone can recompute the decision. No randomness, ever.
  • Cache dimension. Executions served from the compiled-rule cache (HIT) and executions that had to load + compile first (MISS — first call, right after a deploy) are kept as separate distributions by design. Mixing them would hide both signals.
  • 60-second windows. Observations aggregate into 60s windows. Missing windows are shown as genuine gaps — never interpolated.
  • Honest percentiles. Every percentile is accompanied by its sample count n. A percentile is shown only when at least 3 observations sit beyond it (n×(1−q) ≥ 3 — p50 from 6, p95 from 60, p99 from 300 samples); otherwise it is withheld (null in the API, in the console) instead of fabricated.

Slow-rule judgment — relative only

A rule is flagged when its p50 is at least 10× the median of per-rule p50s within the same group, version, phase, and cache state. Judgment requires a cohort of at least 3 rules with n ≥ 100 each. This judgment gate is separate from the per-percentile display gate — a rule can be flagged while its p99 is still withheld. Two deliberate choices:
  1. No absolute thresholds. “Slow” depends on your domain — 50ms may be fine for a batch pipeline and unacceptable for checkout. LexQ never hard-codes a millisecond limit, anywhere.
  2. Median, not mean. With rules at 1, 1, 1, 1 and 40ms, the mean (8.8ms × 10 = 88ms) lets the 40ms rule hide behind the average it inflated. The median (1ms × 10 = 10ms) catches it.

Console

Open a policy group and select the Performance tab.
  • Presets 1h / 24h / 7d, a HIT / MISS cache toggle, and per-percentile colors (p50 / p95 / p99) used consistently across cards, tables, and charts.
  • The rule table shows samples, percentiles, the multiple vs. baseline (×1.2), and a Slow badge when flagged.
  • Click a rule for the detail view: a summary per phase × cache state (merged over the window), plus a dot-per-window time series. Dots are raw window values; empty stretches mean nothing ran.

API

Two read endpoints under the partner API (x-api-key auth):
Query parameters (both): versionId (default: live version), from / to (ISO-8601 instants, half-open range [from, to), default: last 24h), and cacheState (HIT | MISS, default HIT, overview only). An empty or inverted range (fromto) returns error P-035. Response semantics worth knowing:
  • A percentile field is null when its display gate (n×(1−q) ≥ 3) is not met; n is always present. Mixed responses — p50 present, p99 null — are normal.
  • baselines[].status is INSUFFICIENT_COHORT until 3 qualifying rules exist.
  • droppedRows counts corrupted histogram rows that were skipped — losses are counted, never hidden.

CLI & MCP

Two MCP tools expose the same data to AI agents: lexq_profile_overview and lexq_profile_rule.

Overhead & retention

Profiling is designed to never compete with your decisions:
  • Measured overhead at the default 1% sampling: +0.12% on engine p50 (JMH, 20-rule fixture). Instrumentation failures are isolated and counted — they never propagate to the decision path.
  • Windows are queryable for 35 days, then archived. Available on every plan.
Per-rule detail comes from a 1% sample, so low-traffic groups accumulate rule-level percentiles slowly — the group total is recorded on every call regardless. A is the system refusing to guess, not a missing feature.