recca0120/laravel-parallel
Laravel Parallel speeds up Laravel test suites by running PHPUnit tests across multiple processes. It provides a simple Artisan command, configurable concurrency, and familiar PHPUnit output, helping large suites finish faster while staying integrated with Laravel.
Install with Composer (auto-discovery registers the service provider):
composer require recca0120/laravel-parallel
Before writing any code, verify the runtime can actually fork — this package leans on pcntl and posix, which are Unix-only and frequently disabled on shared hosting:
php -m | grep -E 'pcntl|posix'
If either is missing, plan a sequential fallback (see Gotchas). Windows is a non-starter.
Minimal first use case — run independent closures concurrently and collect results in order:
use Recca0120\LaravelParallel\Facades\Parallel;
$results = Parallel::run([
fn () => DB::table('users')->count(),
fn () => DB::table('orders')->sum('total'),
fn () => Http::get('https://api.example.com/health')->json(),
]);
Where to look first:
Parallel facade/class and its config (concurrency, timeout, polling/sleep).spatie/async Pool docs — that engine dictates the semantics you'll actually live with.php artisan command or a dedicated App\Support\ParallelRunner service instead of leaving it inline in a controller.Best first real use case: a report/aggregate command or a cache-warming job where you fan out N independent pieces of work and join the results.
ParallelRunner::run(array $tasks) so controllers and jobs stay thin, and so you can swap a sequential implementation in tests without pcntl.Parallel::concurrency(5) (or the config default) to roughly min(CPU cores, DB pool size, target API's rate limit). Oversubscribing turns parallel into thrash.Bus::batch/queues for retryable, durable jobs. Don't treat Parallel::run as a queue substitute.['ok' => true, 'data' => ...] (or ['ok' => false, 'error' => ...] on catch) so one failure doesn't collapse into an ambiguous null in the results array.array_map(fn ($c) => fn () => process($c), $chunks) trades memory for fewer forks.app(), facades, and injected services resolve inside children — but from the snapshot taken at fork time (see Gotchas).config() writes, and static properties are per-child copies. Reconnect the DB inside each task if you see "server has gone away" or "packets out of order" — children inherit an already-open PDO handle.null crossing the IPC boundary. Return scalars, arrays, or plain DTOs.try/catch and encode the exception (message + stack) into the return payload.pcntl_fork is Unix-only. Gate CI and production; add a sequential fallback for Windows laptops and hosts where the extension is disabled. Never let unit tests depend on pcntl.Parallel::run) multiplies the count and can hit RLIMIT_NPROC, container PID caps, or memory limits. Keep concurrency ≤ CPU cores for CPU-bound work.dd()/dump()/echo from children land on the shared TTY and mix together. Log with a task id on a dedicated channel (Log::channel('parallel')->info("[task {$id}] ...")) and capture stack traces in the returned payload.once()/singletons reflect parent state. If correctness matters, rebind or re-resolve inside the task rather than trusting the inherited instance.config('parallel') to see effective defaults; override per call (->concurrency(), timeout) on hot paths instead of changing the global config.spatie/async changelog on every upgrade, since pool behavior defines this package's semantics more than its own README does.How can I help you explore Laravel packages today?