Laravel Octane Integration
Leakless provides first-class, zero-configuration integration with Laravel Octane (running with the FrankenPHP server driver).
Note: Integration with RoadRunner and Swoole drivers is planned on the project roadmap.
How It Works
When themattosdev/leakless is installed in a Laravel application:
- Auto-Discovery: Laravel's package discovery automatically loads
TheMattos\Leakless\Integrations\Laravel\LeaklessServiceProvider. - Lifecycle Hooks: Leakless automatically registers event listeners on Octane's lifecycle:
Laravel\Octane\Events\WorkerStarting/OctaneStarted➔ Captures the clean initial memory baseline ($M_0$) post-boot.Laravel\Octane\Events\RequestReceived➔ Takes request snapshot of active PDO connections and Linux kernel memory state.Laravel\Octane\Events\RequestTerminated➔ Audits uncommitted PDO transactions, triggers automatic rollback if dangling transactions exist, restores output buffers and timezones, and evaluates relative memory drift against the baseline.
- Graceful Worker Recycling with Cooldown: If memory drift persists across $N$ consecutive requests post-GC, Leakless safely stops the worker after completing the active response, respecting the recycling cooldown window to prevent restart storms.
Configuration
Publish the default configuration file:
bash
php artisan vendor:publish --tag="leakless-config"This creates config/leakless.php:
php
return [
'enabled' => env('LEAKLESS_ENABLED', true),
'max_drift_mb' => env('LEAKLESS_MAX_DRIFT_MB') !== null ? (int) env('LEAKLESS_MAX_DRIFT_MB') : 64,
'max_rss_mb' => env('LEAKLESS_MAX_RSS_MB') ? (int) env('LEAKLESS_MAX_RSS_MB') : null,
'consecutive_violations' => (int) env('LEAKLESS_CONSECUTIVE_VIOLATIONS', 5),
'recycle_cooldown' => (int) env('LEAKLESS_RECYCLE_COOLDOWN', 10),
'trigger_gc' => env('LEAKLESS_TRIGGER_GC', true),
'drift_jitter' => (int) env('LEAKLESS_DRIFT_JITTER', 10),
'max_requests' => env('LEAKLESS_MAX_REQUESTS') ? (int) env('LEAKLESS_MAX_REQUESTS') : null,
'check_transactions' => env('LEAKLESS_CHECK_TRANSACTIONS', true),
'check_file_descriptors' => env('LEAKLESS_CHECK_FILE_DESCRIPTORS', false),
'auto_recycle' => env('LEAKLESS_AUTO_RECYCLE', true),
'log_violations' => env('LEAKLESS_LOG_VIOLATIONS', true),
'zts_aware' => env('LEAKLESS_ZTS_AWARE') !== null ? filter_var(env('LEAKLESS_ZTS_AWARE'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) : null,
'thread_tolerance_mb' => (float) env('LEAKLESS_THREAD_TOLERANCE_MB', 2.0),
'unattributed_violations_threshold' => (int) env('LEAKLESS_UNATTRIBUTED_THRESHOLD', 10),
'resettables' => [
// App\Services\CartSession::class,
],
];Environment Variables
| Variable | Type | Default | Description |
|---|---|---|---|
LEAKLESS_ENABLED | bool | true | Enable or disable Leakless auditing. |
LEAKLESS_MAX_DRIFT_MB | int|null | 64 | Allowable relative RSS drift (MB) above worker baseline. |
LEAKLESS_MAX_RSS_MB | int|null | null | Hard emergency physical RSS ceiling in MB (optional). |
LEAKLESS_CONSECUTIVE_VIOLATIONS | int | 5 | Consecutive post-GC breaches required before recycling. |
LEAKLESS_RECYCLE_COOLDOWN | int | 10 | Minimum interval in seconds between worker recycles. |
LEAKLESS_TRIGGER_GC | bool | true | Trigger gc_collect_cycles() on suspected drift breach. |
LEAKLESS_DRIFT_JITTER | int | 10 | Jitter percentage to desynchronize worker restarts. |
LEAKLESS_MAX_REQUESTS | int|null | null | Maximum request count per worker before recycling. |
LEAKLESS_CHECK_TRANSACTIONS | bool | true | Detect and automatically roll back open PDO transactions. |
LEAKLESS_CHECK_FILE_DESCRIPTORS | bool | false | Inspect /proc/self/fd for lingering file handles and sockets. |
LEAKLESS_AUTO_RECYCLE | bool | true | Automatically signal Octane worker stop on confirmed breach. |
LEAKLESS_LOG_VIOLATIONS | bool | true | Log diagnostic warnings when leaks or anomalies occur. |
LEAKLESS_ZTS_AWARE | bool|null | null | Enable ZTS thread-safe memory attribution (null for auto-detect). |
LEAKLESS_THREAD_TOLERANCE_MB | float | 2.0 | Minimum Zend MM growth (MB) to attribute drift to the thread. |
LEAKLESS_UNATTRIBUTED_THRESHOLD | int | 10 | Consecutive checks before recycling for native C/unattributed leaks. |
State Reset: Octane Flush vs. Leakless Resettables
In persistent Laravel Octane applications, handling mutable state in singletons or legacy services is a common challenge. You have multiple options to manage state reset:
1. Laravel Octane Native Mechanisms
- Scoped Bindings: Register services via
$this->app->scoped(UserContext::class), flushing instances between requests. - Octane Flush List: Add service class names to
config/octane.phpunder the'flush'key to forget instances on request termination.
2. Leakless Resettables Engine
resettablesinconfig/leakless.php: Register class strings (to resetstaticproperties/methods), object instances, or callbacks that need cleanups.- Declarative
#[ResetOnRequest]Attribute: Annotate properties or classes within those registered targets to automatically specify fallback values (default: ...) or custom cleanup methods (resetter: '...'). - Zero-Reflection in Hot Path: Compiles reset closures at worker startup, ensuring pure native execution during request cycles.
Which One Should You Use?
Leakless is designed to be fully modular and flexible. It is at your own discretion which approach to use:
- You can use Laravel Octane's native
scoped()and'flush'mechanisms to recreate entire service instances. - You can use Leakless's
resettables(with or without#[ResetOnRequest]) for fine-grained property resets on existing singletons or legacy callbacks. - Or you can combine both seamlessly in the same application.
Laravel HTTP Testing Macros
When themattosdev/leakless-dev is installed, Leakless injects custom assertion macros into Laravel's TestResponse:
php
test('checkout endpoint leaves clean worker state', function () {
$response = $this->postJson('/api/checkout', [
'cart_id' => 1001,
]);
$response->assertOk()
->assertNoDanglingTransactions()
->assertNoMemoryDrift(maxAllowedMb: 0.25)
->assertCleanWorkerState();
});