DEPLOY.RUN.TIME_BUDGET_EXCEEDED — The post-deploy run took longer than you allow
- Category: performance
- Severity: low — the reader's attention belongs to the schema; this is about the tool
- Level: 0
- Downtime class:
online— the complaint is entirely about the clock; nothing is held or rewritten - Stability: stable
- Suites: deploy
- Applies to: PostgreSQL, MySQL
What it reports
sqlens:postdeploy finished, and it took longer than sqlens.deploy.postdeploy.budget_ms allows.
Nothing about your database is wrong. This is the one finding in the package that is a statement about SQLens itself, and it says so in its own message rather than leaving a reader to work it out while scanning a report full of schema problems.
The message carries two numbers and a split:
This post-deploy run took 7412 ms against a budget of 5000 ms. … Per check, most expensive first:
DEPLOY.LEGACY.CONSTRAINT_NOT_VALIDATED=4980ms, DEPLOY.LEGACY.INVALID_INDEX=1204ms, …
Why a budget at all
A post-deploy run hangs off the end of every deploy. That gives it exactly one survival condition: it must not be something people wait for.
A gate that visibly delays a deploy gets configured away in the first sprint, and a gate nobody runs has helped nobody. Every guarantee in this package is worth precisely nothing on a step somebody commented out — so the budget is not a nicety, and an overrun is not a log line. It travels through the reporters, with a severity, into whatever your team archives.
Why it reports instead of aborting
Stopping at the deadline would destroy the only information that says what to fix. A run cut short produces a report that is short for a reason nobody can see, and the first response to a budget that breaks is then to raise the number — because there is nothing else to act on.
So the run finishes. Checks the budget stopped before they started are separately reported as
undetermined with that reason named; this finding is the run's own account of why that happened.
The exit code does move. sqlens:postdeploy blocks nothing — the deploy has already happened, and
its exit code has always been a report about work already done rather than a refusal to start
anything — but a fail your pipeline is never told about is a fail nobody acts on.
Fix where it is slow, not by raising the number
The split is in the message for exactly this reason. A budget nobody can breach is not a budget, and the number is the last thing to change rather than the first.
That said, raising it is a legitimate answer when the run genuinely has that much to do — a large catalog, many constraints, a distant server. Say so deliberately:
// config/sqlens.php
'deploy' => [
'postdeploy' => [
// Milliseconds, like every other budget here. `--budget` overrides it for one run.
'budget_ms' => 8000,
],
],
What it deliberately does not answer
- It measures wall clock. A loaded machine, a slow network hop or a busy server move the number as surely as a slow check does. The per-check split beside it is what separates the two, and that is why the split is in the message rather than only in the run header.
- It never blames a check. The split names the most expensive one, which is where to look — not what is at fault.
--expect-shadowis outside the budget. That option provisions a database and replays your whole migration history into it; its cost is dominated by the size of that history rather than by anything SQLens does. Counting it would fire this finding on every run that asked for it — a complaint about a cost you chose, printed beside the answer you chose it for.
sqlens:drift has no budget, and that is a decision
The same argument that makes a budget essential for sqlens:postdeploy makes one dishonest for
sqlens:drift.
drift builds its expectation by replaying your migrations into a shadow database. That is
dominated by how much migration history you have — a project with 40 migrations and one with 4,000
are not the same measurement, and neither number is about SQLens. Any figure written here would be
invented, and an invented budget is worse than none: it goes red on projects that are behaving
perfectly, and the first fix anybody reaches for is to delete the check.
The honest statement is about where the command belongs instead:
sqlens:driftis not a step in the deploy script's hot path. It is a CI or cron-adjacent run, outside the deploy window, where taking a minute costs nobody anything.
If you want the drift comparison inside a deploy, that is what sqlens:postdeploy --expect-shadow
is — and it is off by default, behind the production guard, and deliberately outside this budget for
the reason above.