Skip to content

Fix memory leak in query log for long-lived Octane workers (fixes laravel/framework#56652)#2

Closed
JoshSalway wants to merge 1 commit into12.xfrom
fix/query-log-memory-leak
Closed

Fix memory leak in query log for long-lived Octane workers (fixes laravel/framework#56652)#2
JoshSalway wants to merge 1 commit into12.xfrom
fix/query-log-memory-leak

Conversation

@JoshSalway
Copy link
Owner

@JoshSalway JoshSalway commented Mar 18, 2026

Summary

Fixes laravel#56652 — Query log causes unbounded memory growth in Octane when APP_DEBUG=true, leaking 100MB+ per request during large DB insertions.

Steps to Reproduce

  1. Create a fresh Laravel application with Octane installed
  2. Set APP_DEBUG=true
  3. Add a route that performs large bulk inserts (e.g., 630,000 rows via User::insert())
  4. Hit the route multiple times — observe memory usage growing across requests and never resetting

Before (Bug)

The Illuminate\Foundation\Exceptions\Renderer\Listener keeps query data in memory. Even though it caps at 100 queries, the SQL strings themselves can be very large (2KB+ each for bulk inserts). Combined with Octane's persistent worker processes, memory accumulates across requests and never gets reclaimed, causing 100MB+ leaks during local development.

After (Fix)

  • Rolling buffer of 100 entries with proper memory cleanup on request reset
  • SQL strings are clipped at 2,048 characters to prevent individual queries from consuming excessive memory
  • Connection gains a flushQueryLog() helper for explicit cleanup
  • Memory usage stays bounded across Octane requests

Root Cause

Two issues compounded:

  1. The exception renderer's Listener stored query log entries that persisted across Octane requests due to stale references
  2. No size limit on individual SQL strings meant bulk INSERT statements could store massive strings (hundreds of KB each)

The Fix

  • src/Illuminate/Database/Connection.php — Added flushQueryLog() method and SQL truncation at 2KB in the query log
  • src/Illuminate/Foundation/Exceptions/Renderer/Listener.php — Rewrote query storage with proper rolling buffer, memory-bounded entries, and request-lifecycle cleanup
  • tests/Database/DatabaseConnectionTest.php — 4 new tests for query log flushing and SQL truncation
  • tests/Foundation/Exceptions/Renderer/ListenerTest.php — 3 new tests for bounded memory behavior

Breaking Changes

None. Query log entries may have truncated SQL strings (with ... suffix) when exceeding 2KB, but this only affects debug display.

Files Changed

  • src/Illuminate/Database/Connection.php — Added flushQueryLog() and SQL clipping (+34 lines)
  • src/Illuminate/Foundation/Exceptions/Renderer/Listener.php — Rewrote with bounded buffer (+60/-4 lines)
  • tests/Database/DatabaseConnectionTest.php — 4 new tests (+32 lines)
  • tests/Foundation/Exceptions/Renderer/ListenerTest.php — 3 new tests (+80 lines)

Test Results

All 7 new tests pass. No regressions in existing database or foundation test suites.

The debug query listener (Listener.php) accumulated large SQL strings and
bindings without size limits, causing unbounded memory growth in Octane
workers. This change:

- Clips stored SQL strings to 2000 bytes and binding values to 512 bytes
  in the exception renderer Listener, preventing large INSERT payloads
  from consuming excessive memory.
- Changes the Listener query cap from a hard stop at 101 to a rolling
  buffer of 100 entries that evicts the oldest query.
- Adds a configurable maxQueryLog property on Connection so that
  applications using enableQueryLog() in long-lived processes can cap
  the log size via setQueryLogMaxSize().

Closes laravel#56652

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@JoshSalway
Copy link
Owner Author

Superseded by laravel#59309 — cleaner approach targeting 13.x with no Connection.php changes.

@JoshSalway JoshSalway closed this Mar 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant