illuminate/concurrency
Laravel's official concurrency component. Run closures in parallel using process pools through the Concurrency facade, collect results with run(), fire background tasks with defer(), and tune drivers and limits. Ideal for fan-out workloads without queues.
What it is: illuminate/concurrency is a first-party Laravel framework component (a read-only subtree split of laravel/framework, which explains the negligible 11-star count — star count here is a packaging artifact, not an adoption signal). It ships a Concurrency facade with two capabilities: run(array $tasks) executes an array of closures in parallel child PHP processes and returns results in submission order, and defer() schedules work to run after the HTTP response has been flushed. There is no broker, daemon, or external service involved.
Architecture fit: It is a latency-optimization primitive, not a job runner. It fits request-path fan-out where N units of work are independent, short-lived, and read-mostly — parallel calls to internal services, parallel independent cache/read lookups, aggregating several data sources for one page. It does not fit durable work, work needing retries or backoff, anything requiring shared state with the parent, or anything transactional. Teams that slot it in as a queue replacement will be unhappy; it should be positioned explicitly alongside, not instead of, the queue.
Integration feasibility: High on paper and low on ceremony. No new infrastructure, no new deployable, no schema change; the dependency arrives with the framework version and the API surface is two methods. The real constraints are environmental and semantic: the host must permit process spawning (proc_open not disabled), defer() needs an SAPI that flushes before termination (php-fpm/FastCGI; semantics differ under Octane, the built-in dev server, and CLI), and — most importantly — task closures cross a process boundary via serialization. That means no capturing of live PDO handles, request-scoped singletons, auth/session context, or in-memory state. Eloquent models may serialize, but they re-hydrate in the child against a fresh connection, so stale-state assumptions are a live hazard.
Technical risk: Moderate, concentrated in three places. First, cost model: each task bootstraps a full framework instance, so per-request overhead and memory scale roughly linearly with fan-out width; unbounded fan-out under load turns into process storms and worker-pool starvation rather than clean errors. Second, failure semantics: a child exception surfaces at the parent only after joining, so partial-failure policy must be designed, not inherited. Third, observability and testability: there is no per-task tracing out of the box, and parallel execution is awkward to unit test, so teams tend to under-test the paths that most need testing.
Key questions:
defer() actually run post-response in our environments, and who is alerted when deferred work fails silently?Stack fit: Native. No Composer addition beyond the framework, no config beyond the package's default driver setting, no infrastructure. It fits the HTTP-serving tier; it is not meaningful inside queue workers or CLI daemons, where defer() has no response to defer past.
Migration path: Incremental and reversible — the ideal shape for a TPM to sponsor. Wrap two or three existing sequential I/O blocks behind a config flag, keep a sequential fallback path compiled in, and compare p95 latency and error rate before and after. Nothing to migrate: no data, no schema, no service cutover, no rollback ceremony beyond flipping the flag. Treat the first endpoint as a measurement, not a rollout.
Compatibility: Pin to the framework minor and never require the split package independently of the framework version — they move together. Verify proc_open availability across local, CI, staging, and production container images before committing; minimal PHP images and hardened runtimes routinely disable it, producing the classic "works on my machine, fatal in prod" failure. Validate defer() behavior under whichever SAPI production uses, and re-evaluate entirely if the platform runs Octane, since long-lived workers change both process-spawn economics and post-response semantics.
Sequencing:
defer() for non-critical post-response work, with an explicit idempotency review and its own alerting.Maintenance: Near-zero for the package itself — release cadence rides Laravel and upgrades are framework upgrades. The maintenance burden you actually own is the closure contracts, which fail at runtime rather than compile time and therefore rot silently. Budget for a review checklist and a handful of contract tests; that is the real ongoing cost, and it is small but non-zero.
Support: First-party, documented, covered by Laravel's support policy, but with no vendor SLA and no dedicated escalation path. Debugging parallel child processes is materially harder than debugging sequential code, so invest early in a parent-to-child correlation ID in logs; without it, incident triage degrades badly.
Scaling: Effective capacity is roughly (worker pool size ÷ tasks per request) concurrent requests, with memory multiplied per worker. In containerized environments this manifests as OOMKills and 502s rather than clean backpressure. Cap fan-out per request, alert on worker saturation rather than request latency, and re-size pools before broadening adoption.
Failure modes to instrument for: child bootstrap failure from non-serializable closures (hard request error); a hung child holding the parent request open until timeout; DB connection amplification when every child opens its own connection — the highest-impact risk and the one most often missed; partial failures surfacing as total failures because the join is all-or-nothing; deferred work failing invisibly after the client already has a 200; and environment drift where process spawning is disabled in one tier only.
Ramp-up: A Laravel-fluent engineer is productive in half a day. The genuine cost is a mental-model shift: developers arriving from Promise.all-style parallelism will assume shared memory and mutate captured objects. Expect one to two sprints of review friction and a realistic chance of one production incident if the serialization and per-child-connection constraints are not documented and socialized before rollout. Write a one-page internal "Concurrency vs. queue" decision note first; it will pay for itself.
How can I help you explore Laravel packages today?