Download the PHP package ericklabs/laravel-query-budget without Composer
On this page you can find all versions of the php package ericklabs/laravel-query-budget. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download ericklabs/laravel-query-budget
More information about ericklabs/laravel-query-budget
Files in ericklabs/laravel-query-budget
Package laravel-query-budget
Short Description Detect SQL query regressions in Laravel tests using configurable query budgets.
License MIT
Homepage https://github.com/erick9125/laravel-query-budget
Informations about the package laravel-query-budget
Laravel Query Budget
Detect SQL query regressions in Laravel tests using configurable query budgets.
A feature can stay functionally green while quietly growing from 5 to 50 database queries. This package measures the SQL executed inside a scope and fails the test when count, duplication, or duration exceeds the limits you define.
Español
Why this package
Most tools help you inspect queries. Query Budget helps you guard them.
| Concept | Role |
|---|---|
| Budget | Optional limits for queries, duplicates, slow queries, and duration |
| Regression | A change that still passes assertions but spends more SQL than before |
| Fingerprint | Stable identity for a query shape (structural) or shape + bindings (exact) |
Focus of the package:
- Count queries
- Detect exact duplicates
- Detect slow queries
- Measure accumulated SQL time
- Enforce budgets in tests
- Produce readable failure output
- Work with Pest and PHPUnit
- Support MySQL, PostgreSQL, and SQLite in tests
It is a test-time guard, not a dashboard, Debugbar plugin, AI advisor, or production APM.
Measure queries inside a test. Fail when they exceed a configured budget.
The 10-second pitch
Problem
Solution
Result
That failure message is part of the product.
Requirements
| Requirement | Version |
|---|---|
| PHP | ^8.2 |
| Laravel | ^11.0 or ^12.0 |
| Databases in tests | MySQL, PostgreSQL, SQLite |
| PHP | Laravel 11 | Laravel 12 |
|---|---|---|
| 8.2 | Yes | — |
| 8.3 | Yes | Yes |
| 8.4 | — | Yes |
Compatible with Pest and PHPUnit.
Installation
Laravel package auto-discovery registers the service provider and facade.
Publish the config if you want local defaults:
Quick start
Facade assertion
Every limit is optional. Assert only what matters for that test.
Pest / PHPUnit trait
Measure without failing
Use this to inspect baselines before tightening budgets:
What gets measured
| Budget option | Meaning |
|---|---|
maxQueries |
Maximum SQL statements in the scope |
maxDuplicates |
Maximum exact duplicate executions |
maxRepeatedPatterns |
Maximum distinct repeated patterns (possible N+1 signals) |
maxSlowQueries |
Maximum queries at/above the slow threshold |
maxTotalDurationMs |
Maximum accumulated SQL time |
maxSingleQueryDurationMs |
Maximum duration for any single query |
slowQueryThresholdMs |
Duration at which a query counts towards maxSlowQueries (defaults to config) |
possibleNPlusOneThreshold |
Threshold used for repeated-pattern signals |
maxSingleQueryDurationMs and maxSlowQueries are independent: the first caps the slowest single query, the second counts how many queries cross slowQueryThresholdMs.
Exact duplicates vs repeated patterns
These are not the same metric.
Exact duplicate — same normalized SQL and same bindings:
Repeated query pattern — same shape, different bindings (possible N+1 signal):
Example signal:
The package reports a possible N+1 pattern. It does not claim infallible N+1 detection.
Patterns are enforceable, not just informational:
How it works
Each recorded query keeps:
- SQL
- bindings
- duration (ms)
- connection name
- optional scope label
SQL and bindings stay separate so fingerprints stay stable and sensitive values are not forced into public reports.
Fingerprints
| Type | Built from | Used for |
|---|---|---|
| Structural | Normalized SQL | Repeated patterns / possible N+1 |
| Exact | Normalized SQL + serialized bindings | True duplicates |
Normalization collapses whitespace, lowercases SQL, replaces literals with placeholders, and unquotes identifiers so the same query fingerprints identically on MySQL, PostgreSQL, and SQLite. See docs/query-normalization.md.
Test failures
QueryBudget::assert() and assertQueryBudget() register an assertion with PHPUnit, so a test whose entire body is a budget assertion is never flagged as risky. An exceeded budget is reported as a test failure, not an error. See docs/concepts.md.
Isolation rules
- Only queries inside the active measurement scope are counted
- Scopes do not leak into later tests
- If the callback throws, the recorder is cleaned and the original exception is rethrown
- Measurement scopes are designed for isolated synchronous test runs
Configuration
config/query-budget.php:
| Key | Effect |
|---|---|
enabled |
When false, every budget passes without measuring |
warn_when_disabled |
Writes one STDERR notice per process while disabled, so the silent gate is visible without failing the suite |
report_format |
console or json; json is handy as a CI artifact |
connections |
Connection names to measure. Empty measures all of them |
ignore |
Case-insensitive SQL substrings that are never recorded |
Per-assertion options override defaults when provided. There is no universal “slow” number: 40 ms may be slow locally and normal in CI.
Keeping infrastructure noise out
Sessions, Telescope, and migrations run SQL you did not write. Instead of padding maxQueries, drop them:
Ignored queries never enter the scope, so the count a budget sees is the one you asked about.
Custom report format
QueryReportFormatter is resolved from the container, so binding your own changes the failure message:
Real scenario
Eager-loaded listing:
Typically 2 queries.
Lazy-loaded listing:
With 20 posts: about 21 queries.
The package suite reproduces this in tests/Feature/EloquentPatternTest.php.
Architecture overview
Public API prefers value objects, named arguments, and small methods over loosely typed config arrays.
Limitations
Read these before relying on timing budgets in CI.
- Durations are noisy — hardware, Docker, CI load, and database engines change timings. Prefer strict count/duplicate budgets; give duration budgets margin.
- Framework queries appear — sessions, auth, Telescope, seeders, and middleware can add SQL. Keep scopes tight around the code under test.
- N+1 is heuristic — repeated patterns are a signal, not a verdict.
- Synchronous scopes — parallel runners and long-lived workers need extra care; isolate each measurement.
Details: docs/limitations.md
Best practices
- Start with
measure()— inspect real counts before locking a budget. - Prefer count budgets in CI —
maxQueriesandmaxDuplicatesare stable gates; keep timing limits looser. - Budget one behavior per test — assert the endpoint or service under review, not the whole bootstrap.
- Use a clear
scope— labels likeGET /api/ordersmake failures easier to read in CI logs. - Treat repeated patterns as investigation hints — confirm with eager loading or batching before changing production code.
- Keep sensitive data out of reports — public output uses normalized SQL; avoid dumping raw bindings in shared logs.
Good first budgets
| Scenario | Suggested starting point |
|---|---|
Simple show endpoint |
maxQueries: 3, maxDuplicates: 0 |
| Index/list with relations | maxQueries near the eager-load baseline |
| Write action | Count statements after setup/seed work is outside the scope |
| Suspected N+1 | Inspect repeatedPatterns, then lock maxQueries |
Development
CI matrix:
- PHP 8.2 + Laravel 11 (SQLite)
- PHP 8.3 + Laravel 11 (SQLite)
- PHP 8.3 + Laravel 12 (SQLite)
- PHP 8.4 + Laravel 12 (SQLite)
- PHP 8.3 + Laravel 12 (MySQL 8 and PostgreSQL 16)
Run the suite against another driver locally with DB_DRIVER=pgsql vendor/bin/pest.
Documentation
| Doc | Contents |
|---|---|
| concepts.md | Budgets, scopes, duplicates, exceptions |
| query-normalization.md | Fingerprints and reporting safety |
| limitations.md | Timing, framework queries, parallelism |
| README.es.md | Spanish documentation |
| examples/demo-app | Eager vs lazy scenario |
| CHANGELOG.md | Release notes |
| CONTRIBUTING.md | Contribution guide |
| SECURITY.md | Vulnerability reporting |
License
MIT © ErickLabs
All versions of laravel-query-budget with dependencies
illuminate/contracts Version ^11.0|^12.0
illuminate/database Version ^11.0|^12.0
illuminate/support Version ^11.0|^12.0