Skip to content

v3.2.0 — Safe capture foundation (query groups, origin, privacy-first AI payload)

Latest

Choose a tag to compare

@halilcosdu halilcosdu released this 11 Jul 17:00
06143c7

v3.2.0 — Safe capture foundation

The first phase of the 2026 roadmap: make Slower something you confidently leave on in production, and make every capture know what it is (a fingerprint) and where it came from (its origin). No breaking changes — additive migration, existing config/API untouched, synchronous analysis still the default.

Highlights

  • Query fingerprints & the Grouped view. Every capture gets a versioned fingerprint from the parameterized SQL (literals/comments/whitespace/IN (...) normalized in a single lexer-style pass). The dashboard gains an Events | Grouped toggle — one row per query shape per connection, with occurrence count, avg/max duration and last-seen, drilling down to the underlying events. php artisan slower:fingerprint backfills pre-3.2 rows (chunked, idempotent).
  • Origin context. Each capture records its origin — HTTP route/URI/Controller@action, queue job class, or artisan command — plus the first file:line of application code (taken only for threshold-exceeding queries, DEBUG_BACKTRACE_IGNORE_ARGS). Shown on the detail page and fed to the AI prompt. The authenticated user id is opt-in and never forwarded to the LLM.
  • Production controls. capture.sample_rate, capture.max_per_execution, a 60s circuit breaker when storage itself fails, and a hardened self-capture guard.
  • Privacy-first AI payload. Only the parameterized SQL (plus schema, origin, EXPLAIN) leaves the app — table names are extracted from the parameterized SQL so no inlined literal leaks through schema introspection. Raw SQL and bindings are explicit opt-ins; a configured PayloadRedactor covers every outbound path (raw SQL, bindings, and the EXPLAIN plan); a misconfigured redactor throws instead of silently passing secrets.
  • Queued analysis. SLOWER_ANALYZE_QUEUE=<queue> runs analysis as unique-per-record background jobs (dashboard + slower:analyze --queue); unset stays synchronous, no worker required. Jobs drop cleanly if their record was pruned.
  • Events, not lock-in. SlowQueryCaptured and SlowQueryFirstSeen (per-connection identity) — wire alerts to Slack/mail/webhook in a few lines. A throwing listener is reported but never breaks the app query, arms the circuit breaker, or suppresses the other event.

Three new config blocks total: capture, ai_payload, analyze_queue.

Design

Selected from a multi-model council roadmap. Deliberate choices: fingerprints from parameterized SQL (never raw), row-per-event storage kept (aggregate model deferred to v4.0), fingerprint normalizer favors a false split over a false merge (documented escaping tradeoff), origin backtrace paid only on slow queries.

Verification

  • 211 tests / 445–449 assertions green on Laravel 11 (11.45.2), 12 (12.63.0) and 13 (13.19.0) (PHP 8.4). PHPStan level 5 clean, Pint clean.
  • Three rounds of recursive review (inline → workflow → three independent adversarial agents) — each verified finding fixed test-first. Caught and fixed: a raw-SQL literal leak through schema extraction, an event/circuit-breaker coupling, a first-seen scoping mismatch, queued-job resilience, nested-origin attribution, and a build-tooling slip that had leaked dev dependencies into the production require block.
  • Browser-tested with Playwright: events↔grouped toggle, occurrence badges, drill-down, origin panel, the analyze flow, dark/light — no console errors.

Upgrade

Publish and run the new migration, then optionally backfill fingerprints:

php artisan vendor:publish --tag="slower-migrations"
php artisan migrate
php artisan slower:fingerprint

Heads-up: GitHub Actions is still failing to provision runners account-wide (unrelated to this change), so the matrix was validated locally across Laravel 11–13 rather than by CI.