modelslab/octane-coroutine
Adds coroutine support to Laravel Octane, letting you run concurrent PHP tasks inside Octane workers for faster I/O-bound requests. Integrates with Swoole coroutines, offers simple helpers for parallel execution, and helps reduce response times without changing your application structure.
Package identification. The release notes describe a Laravel runtime-integration layer for long-lived PHP workers (Swoole/Octane config surface: octane.swoole.redis_pool, base-application rebinding/terminating callbacks, request-scoped bus dispatcher). All evaluation below is scoped to that role: a package that substitutes for, or wraps, laravel/octane.
Architecture fit. Strong. The two fixes address the single most consequential architectural hazard of running Laravel under a long-lived worker: request state escaping its request. Forwarding refresh()/rebinding()/terminating() to the base application caused (a) unbounded callback accumulation on the worker, (b) the session guard reading the boot request instead of the current one — silently breaking remember-me and basic auth — and (c) terminate() re-running every prior request's callbacks on every subsequent request. That is a monotonic, age-correlated performance decay plus a correctness bug in auth. Any team running this package in production is exposed today; the fix is not a nicety, it is the difference between "works in staging, rots in prod" and a stable runtime.
The afterResponse() fix is the same class of defect with a clearer blast radius: a single dispatch + 40 requests produced 41 executions. For anyone using after-response jobs for email, webhooks, or billing side effects, this is duplicate-execution territory. Treat as a correctness fix with data-integrity implications, not a perf fix.
Integration feasibility. High, with one hard caveat. The behavioral changes require no consumer code changes — the old behavior was unambiguously wrong, and it is difficult to construct a legitimate dependency on it. The Redis connection pool is opt-in and default-off, so it can be adopted on a separate, later decision boundary from the correctness fixes.
The caveat is packaging, not code: v0.8.37 is byte-identical to v0.8.36, and exists only because Packagist indexed v0.8.36 while its tag briefly pointed at the v0.8.35 commit — and still serves that mapping. That means composer update resolving to 0.8.36 gets you the pre-fix code. Any version constraint that permits 0.8.36 (^0.8, ~0.8.36, *) is unsafe and will silently under-deliver.
Technical risk.
terminate()/callback scoping changes worker-level behavior. Low probability of legitimate breakage; high impact if something in your app registered a callback expecting base-app persistence (e.g. a service provider that intentionally registers once per worker from a request path — an anti-pattern, but it exists in the wild).max_idle 16, max_idle_time 60s, ping_after_idle 5s, max_lifetime 300s are all defaults to review against your traffic shape).Key questions for the maintainer / vendor.
octane.swoole.redis_pool intended as generally available or experimental? What is the promotion criterion?SELECT / multi-tenant database switching and AUTH rotation? The notes imply cleanliness is validated — what specifically is checked?0.8.x considered pre-1.0 / unstable, and are minor versions allowed to carry behavior changes of this magnitude?Stack fit. Fits any Laravel 11–13 service on PHP 8.3/8.4 currently running Swoole, RoadRunner, or FrankenPHP workers — and, importantly, fits the default php-fpm/artisan serve case at zero cost, since the fixed code paths are inert outside a long-lived worker. No new infrastructure is required for the correctness fixes. The Redis pool requires only that you already use Redis (session/cache/queue), which is the common case.
Migration path. Two independent steps, deliberately decoupled:
"<vendor>/<package>": "0.8.37" — not a range. Run composer update <package>, then verify with composer show and a diff against the tag that the deployed code is the 0.8.37 tree, explicitly confirming you are not on the stale 0.8.36 mapping. composer.lock must be committed. This step should be doable in a single sprint with a staging soak.octane.swoole.redis_pool.enabled in staging only, after step 1 has soaked. Tune max_idle and max_idle_time against measured concurrency. Treat as an independent go/no-go.Compatibility. Confirm PHP and Laravel versions against 8.3/8.4 and 11/12/13 before committing. Teams on older minors should not assume safety from "no breaking changes listed" — the notes describe untested combinations, not tested-and-fine ones.
Sequencing.
terminating() callbacks fire once and only for their own request.dispatch(...)->afterResponse() runs exactly once across N sequential requests in the same worker.Maintenance. Low ongoing cost, but version pinning discipline is now mandatory rather than nice-to-have. The 0.8.36 Packagist mapping means a loose constraint can silently roll you onto pre-fix code during an unrelated composer update — a genuinely nasty failure mode because it looks like an innocuous dependency refresh. Add a CI check that asserts the installed version and, ideally, the resolved commit.
Support. This release materially reduces support load: the terminate()-replay bug meant a worker slowed down as it aged and ran an ever-growing pile of stale callbacks, which produces exactly the kind of unreproducible, "it's fine after a restart" incident that consumes disproportionate on-call time. Fixing the session-guard-reads-boot-request bug also removes a class of intermittent auth failures that are typically misattributed to the identity provider or the browser.
Scaling. The headline number is the strongest part of this release: Redis connections per request 2.0 → 0.004, with flat throughput when worker recycling is disabled. That has three concrete operational consequences:
Cannot assign requested address) under load disappears as a failure mode — previously a hard ceiling on request rate per node, now removed.Failure modes to instrument.
max_idle_time is wrong, or that a code path is holding a connection across an await/queue boundary.max_idle at 16, a concurrency spike may exceed idle supply and fall back to opening connections; confirm fallback behavior is degrade-not-fail.Ramp-up. Engineering ramp-up is near zero: the correctness fixes are transparent, and the pool is one config flag. The real ramp-up is on-call and platform: whoever owns the workers needs to learn to read per-worker age, RSS, Redis connection counts, and lease-exception rates as a coherent story, and needs a documented procedure for draining a pool during deploy/Redis maintenance. Budget a short runbook update and a dashboard panel for the pool — small, but non-zero, and worth doing before enabling the flag rather than after the first incident.
How can I help you explore Laravel packages today?