Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Octane Coroutine Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

This package provides request-scoped application state for long-running workers (Octane/Swoole), plus optional worker-level resource pooling. Install/pin v0.8.37 or later — do not rely on v0.8.36, since Packagist indexed that version while its tag pointed at the v0.8.35 commit and still serves that mapping:

composer require <vendor>/<package>:^0.8.37

First places to look:

  1. The package config (typically published to config/octane.php or a package-specific config file). Confirm the new block exists:
    'swoole' => [
        'redis_pool' => [
            'enabled' => false, // opt-in, default off
            'max_idle' => 16,
            'max_idle_time' => 60,
            'ping_after_idle' => 5,
            'max_lifetime' => 300,
        ],
    ],
    
  2. The bootstrapper/worker entry point that wraps the base application per request — this is where refresh(), rebinding(), and terminating() are intercepted. If you have custom bootstrappers, check they use the request's application instance, not app() captured at worker boot.

First use case: run your app under the worker with a single worker and a handful of requests, and confirm nothing accumulates. Before v0.8.37, each request leaked a terminating/rebinding callback onto the worker, so a simple loop of 40 requests was enough to see the drift (session/remember-me behaving oddly, terminate() firing for old requests, throughput falling off as the worker aged). After upgrading, that loop should be flat.

Second use case: enable the Redis pool on a staging worker and watch new-connection counts. The release notes report 2.0 → 0.004 new connections per request in production, with Cannot assign requested address errors disappearing under load.

Implementation Patterns

Treat the request application as the only application. Anything you register during a request — event listeners, rebinding callbacks, terminating callbacks, macros — must land on the request-scoped instance. The v0.8.37 fix makes the package forward refresh()/rebinding()/terminating() to the per-request app rather than the base app. In your own service providers and middleware, prefer the injected $app/container over a statically captured app() from worker boot:

public function register(Application $app): void
{
    $app->rebinding('request', function ($app, $request) {
        // scoped to this request only
    });
}

After-response work. dispatch(...)->afterResponse() and batches are now request-scoped via the bus dispatcher. Pattern: use them freely in controllers, but never resolve the dispatcher once at worker boot and stash it in a singleton — that reintroduces the leak the fix removes. If you have a long-lived service that dispatches, inject the dispatcher (or resolve it per call) so it comes from the current request's container.

Enable the Redis pool deliberately, per environment. Start with defaults, measure, then tune:

'redis_pool' => [
    'enabled' => env('OCTANE_REDIS_POOL', false),
    'max_idle' => 16,
    'max_idle_time' => 60,
    'ping_after_idle' => 5,
    'max_lifetime' => 300,
],

Rules of thumb: max_idle should cover your peak concurrent Redis users per worker (queue listeners, cache, session all count); max_idle_time shorter than your Redis server's timeout; ping_after_idle below any NAT/firewall idle drop; max_lifetime as a backstop against half-dead sockets. If you use multiple Redis connections (cache, session, queue, Horizon), expect the pool to be sized per connection, not globally.

Verification workflow. Add a CI job (or local script) that boots the worker, issues N requests, and asserts stable behaviour:

  • request N+1 sees its own request in the session guard (test remember-me and HTTP basic auth explicitly — these read the request and were broken before),
  • a terminate() side effect registered on request 1 does not fire on request 40,
  • a single afterResponse() dispatch produces exactly one execution (pre-fix: 1 dispatch + 40 requests → 41 runs),
  • INFO stats / CLIENT LIST on Redis shows a flat connection count.

The release was verified against PHP 8.3/8.4 and Laravel 11/12/13 with a Redis service in the matrix — mirror that combination in your own CI before rolling to production.

Gotchas and Tips

  • Version pinning matters here. Because of the Packagist/tag mismatch, composer require could silently give you v0.8.35's code under a v0.8.36 label. Require ^0.8.37, and after upgrading run composer show <vendor>/<package> and confirm the installed version and reference. If you already have v0.8.36 installed, force a reinstall (composer update <vendor>/<package>) rather than assuming you have the fixes.
  • The leak fixes are silent fixes. There's no deprecation or API change to alert you — behaviour simply changes. Code that accidentally depended on base-app callbacks (e.g. a terminating callback that ran on every subsequent request and "worked" as a periodic cleanup) will now run exactly once. Audit anything that looked like an accidental cron.
  • Session guard / auth regressions are the tell. If remember-me tokens or Auth::basic() start reading an empty request after an upgrade or a custom bootstrapper change, you're back on the base app's boot request. Check that refresh() is reaching the request-scoped instance.
  • Blade registers a terminator per request. Under Octane this used to be the fastest-growing part of worker state. If you see memory or per-request time creeping upward over a worker's life, suspect terminating callbacks first, then container bindings.
  • Pool leases are strict by design. Only provably clean sockets are returned to the pool, and holding a lease past its request throws instead of sharing the socket. That throw is a bug in your code, not a pool misconfiguration — look for a connection retained in a singleton, a static, a closure captured by a queue job, or a generator left suspended.
  • Don't hold a pooled connection across an await/yield or a Coroutine::sleep. It survives the request boundary and will trip the lease guard. Grab connections at the point of use.
  • Long-running queue workers and the pool interact. The pool is per-worker and per-request; a queue:work process inside the same worker competes for the same idle sockets. Size max_idle for the sum, not the web path alone.
  • The pool is off by default for a reason. Enable it in staging first, then production; the failure mode when the Redis server or a proxy silently drops idle connections is a ping_after_idle that's too long. If you see sporadic Connection reset by peer after enabling, lower ping_after_idle and max_idle_time.
  • Debugging tools that work well here: an Octane tick/request listener that logs memory_get_usage() and the container's binding count per request (should be flat), redis-cli CLIENT LIST | wc -l sampled over time, and a two-request integration test asserting callback counts. Log request IDs through terminate() to catch cross-request bleed.
  • Extension points: the bus dispatcher is the seam for after-response behaviour — bind your own only if you preserve request scoping; the Redis pool config block is the seam for connection reuse. Both are intentionally narrow, which is good: avoid wrapping them in your own worker-boot singletons, since that would recreate the exact class of bug this release fixes.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route