After a release a Laravel news app feels slower; how do Pulse's slow-request and slow-query recorders and thresholds pinpoint the culprit endpoint?
answer
- SlowRequests: default threshold 1,000 ms
- keyed by method, route URI, action
- regex thresholds with a 'default' key
- SlowQueries: SQL plus file:line location
- compare periods before and after release
basics
~20 sPulse's SlowRequests recorder logs requests at or above a threshold (1,000 ms by default), grouped by method, route URI and controller action; SlowQueries groups slow SQL with the file and line that ran it. Compare the cards before and after the release.
solid answer
~50 sThe `SlowRequests` recorder measures each routed request and records it when the duration is not under its threshold, which defaults to 1,000 ms (`PULSE_SLOW_REQUESTS_THRESHOLD`). Each entry is keyed by HTTP method, route URI pattern and controller action, so `GET /articles/{article}` is one row with a count and the slowest time, however many slugs were hit. On the news app I would open the Slow Requests card for the period since the release and look for a route that is new to the list or whose count jumped. Then the Slow Queries card, keyed by SQL without bindings plus the `file:line` in app code when `location` is on, usually names the query behind it. The `threshold` can be an array of regexes with a `default`, so I can lower it for `#^/articles/#` to catch smaller regressions and raise it for known-slow admin routes.
code
php · 21 lines<?php
use Laravel\Pulse\Recorders;
return [
// ...
'recorders' => [
Recorders\SlowRequests::class => [
'enabled' => env('PULSE_SLOW_REQUESTS_ENABLED', true),
'sample_rate' => env('PULSE_SLOW_REQUESTS_SAMPLE_RATE', 1),
'threshold' => [
'#^/articles/#' => 300,
'#^/admin/#' => 5000,
'default' => env('PULSE_SLOW_REQUESTS_THRESHOLD', 1000),
],
'ignore' => [
'#^/pulse$#',
],
],
],
];go deeper
Recall that Pulse has Slow Requests and Slow Queries cards and that both use a 1,000 ms default threshold.
Explain how SlowRequests keys entries by method, route URI and action, how regex thresholds with a default work, and how to read the cards across a release.
Show a method for a post-release regression: compare periods, correlate routes with query locations and outgoing calls, and tune thresholds without drowning the cards.
Decide which endpoints deserve tighter thresholds based on user impact, and when Pulse's aggregates are not enough and a trace-level tool is needed.
## The scenario A news site ships a release on Tuesday morning. By lunch, editors say article pages feel sluggish, but nothing is failing. **Laravel Pulse** is built for exactly this question: *which endpoint got slower, and why?* Two recorders do most of the work: `SlowRequests` and `SlowQueries`. ## How SlowRequests decides what to record The recorder hooks the HTTP kernel so it runs once the request lifecycle ends, then for each request: 1. Skips requests that did not match a route. 2. Applies the recorder's `sample_rate` lottery (1 by default, so every request). 3. Resolves the **path** as the route's domain plus its URI pattern, and the **via** as the route's action name. 4. Skips paths in the `ignore` list (by default the Pulse dashboard and `/telescope`). 5. Skips the request if its duration is **under** the threshold for that path; anything at or above it is recorded. The recorded key is the method, path and action, and Pulse aggregates the **maximum** duration and a **count** for it. Because the path is the URI pattern, `GET /articles/{article}` is one row whether ten or ten thousand different slugs were slow. For Livewire update requests, Pulse attributes the request to the page the component lives on and shows the Livewire endpoint as the via. ## Thresholds per route The default threshold is 1,000 ms, set by `PULSE_SLOW_REQUESTS_THRESHOLD`. The `threshold` key can instead be an array: - keys are regular expressions matched against the path (or, for other recorders, the SQL, job class or URL); - the first matching pattern wins; - `default` applies when nothing matches. For a news site you might lower article pages to 300 ms, so a regression is visible well before it crosses a full second, and raise `/admin/` exports to 5,000 ms so they stop drowning the card. ## Correlating with slow queries `SlowQueries` listens to every `QueryExecuted` event. Its entries are keyed by the SQL text without bindings plus, when `location` is true (the default), the first `file:line` outside the framework and Pulse. That means the card can say "this `select ... from comments where article_id = ?` from `app/Http/Controllers/ArticleController.php:48` now takes 2.3 s". Its threshold also defaults to 1,000 ms (`PULSE_SLOW_QUERIES_THRESHOLD`) and accepts the same regex array. ## A working method 1. Use the dashboard's period selector (1 hour, 6 hours, 24 hours or 7 days) to see which rows appeared or grew since the release. 2. On the **Slow Requests** card, look for a route that is new to the list or whose count jumped; note its action. 3. On the **Slow Queries** card, look for SQL whose location is in that controller, or in code the release touched. 4. Check **Slow Outgoing Requests** as well, since a new call to an external API made with Laravel's HTTP client shows up there with its own 1,000 ms default threshold. 5. Temporarily lower the threshold for the suspect route if the regression is real but still under a second. | Card | Keyed by | Default threshold | Tells you | |---|---|---|---| | Slow Requests | method, route URI, action | 1,000 ms | which endpoint | | Slow Queries | SQL, location | 1,000 ms | which query and where | | Slow Outgoing Requests | URL (groupable) | 1,000 ms | which external call | ## Limits to keep in mind - Pulse stores aggregates, not individual traces, so it tells you **where** to look, not the full story of one request. - A threshold set too high hides a regression that doubled latency from 400 ms to 800 ms. - Sampling the slow recorders can hide rare slow events entirely, because the sampling lottery runs before the threshold check. ## Reading the numbers - The **count** is how many requests crossed the threshold in the selected period, not total traffic; a route with a small count but a huge maximum is usually one pathological input, such as an article with thousands of comments. - The **maximum** is a single worst case, so one outlier can dominate it; check whether the count grows with it. - A route that appears only after the release, whose action points at a controller the release changed, is the strongest lead. - Rows for Livewire updates show the page path with the Livewire endpoint as the via, so a slow component is traced back to its page.
- Why does Pulse show one row for GET /articles/{article} instead of one per article URL?The SlowRequests recorder keys entries by method, the route's URI pattern (with its domain, if any) and the action name, not by the concrete URL. That groups every slug into one row with a count and a maximum, which is what you want for spotting a slow endpoint; the individual URLs are not stored.
- The same slow query appears twice on the Slow Queries card; why?With `location` enabled, the key is the SQL plus the first app `file:line` that ran it, so the same statement issued from two places becomes two rows. That is useful for finding the caller; set `PULSE_SLOW_QUERIES_LOCATION=false` if you would rather group purely on the SQL.
saying these in an interview costs you the question
- Pulse's slow-request card lists every slug of a route as a separate row.
- The default slow-request threshold is 100 ms.
- Per-route thresholds need a separate recorder class for each route.
- Pulse stores a full trace of each slow request, like a profiler.
- Slow queries are grouped with their bindings, so each article id is a new row.