Skip to main content

Cos’è la concorrenza

Se esegui in sequenza più operazioni indipendenti (chiamate ad API esterne, aggregazioni DB, ecc.), il tempo totale è la somma di ciascuna. Eseguendole in parallelo il tempo totale si riduce al tempo dell’operazione più lenta. La facade Concurrency di Laravel realizza questo con un’API semplice.
La facade Concurrency è stata introdotta in Laravel 11 ed è disponibile anche in Laravel 13. Il driver predefinito usa processi PHP figli e non richiede pacchetti aggiuntivi.

Come funziona

La facade Concurrency serializza le closure passate, le invia a un comando Artisan nascosto e le esegue come processi PHP separati. Al termine i valori di ritorno vengono serializzati e restituiti al processo genitore. Sono disponibili tre driver:

Uso di base

Concurrency::run()

Passa a run() un array di closure: verranno eseguite in parallelo. Ottieni i risultati come array nello stesso ordine delle closure.

Scegliere il driver

Con driver() specifichi un driver.
Per cambiare il driver predefinito, pubblica la configurazione e aggiorna default.

Driver fork

fork è più veloce di process ma disponibile solo in CLI (Artisan, queue worker). Non funziona nelle richieste web. Prima installa spatie/fork.
fork non funziona nelle richieste web. Usalo in comandi Artisan o worker.

Senza risultati: Concurrency::defer()

Se non ti interessa il risultato e vuoi eseguire dopo l’invio della risposta HTTP, usa defer().
Le closure non vengono eseguite al momento della chiamata, ma dopo che la risposta è stata inviata.
defer() è ideale per raccolta di analytics o cache warming: attività che non richiedono di far attendere l’utente.

Esempio pratico: chiamare più API in parallelo

Dashboard di e-commerce che consulta tre API (inventario, vendite, spedizioni).

Sequenziale (prima)

Parallela (dopo)

Con tre API da 1s ciascuna: sequenziale 3s, parallelo ~1s.

Aggregazioni DB in parallelo

Con process ogni figlio apre una nuova connessione DB. Attenzione al limite di connessioni.

Propagazione automatica del Context

In Laravel 13.31 (#61419) il driver process ha iniziato a propagare automaticamente il Context corrente ai processi figli. In precedenza, i valori impostati con Context::add() non venivano ereditati dai processi figli avviati con Concurrency::run() o Concurrency::defer(), e il context stampato nei log poteva risultare incoerente fra processo padre e figlio. Dopo la correzione, internamente viene usato lo stesso meccanismo di trasferimento del contesto ai job in coda.
  1. Subito prima di avviare il processo figlio, il risultato di Context::dehydrate() viene serializzato in JSON e passato come variabile d’ambiente __LARAVEL_CONTEXT
  2. Quando il processo figlio (il comando Artisan nascosto invoke-serialized-closure) parte, legge __LARAVEL_CONTEXT e chiama hydrate() sul repository Context
Per questo motivo, il contesto impostato all’esterno della closure può essere consultato direttamente al suo interno.
Il driver fork usa la fork() a livello di sistema operativo fornita da spatie/fork, quindi il processo figlio viene avviato copiando così com’è lo spazio di memoria del processo padre. Per questo il contesto era già naturalmente condiviso e questa correzione riguarda principalmente il driver process (quello che avvia un processo separato).
Se metti nel Context valori usati per correlare i log — come request ID o user ID — puoi tracciare con la stessa chiave anche i log delle elaborazioni parallele avviate con Concurrency::run(). Per l’uso dettagliato consulta Condivisione del contesto.

Nei test

Usa sync per esecuzione sequenziale, evitando l’overhead dei processi.
O in .env.testing:

Note

Le closure vengono serializzate. Non catturare oggetti non serializzabili (connessioni DB, file handle) da fuori: costruiscili dentro la closure.
process ha un costo di avvio dei figli: per operazioni molto brevi (< pochi ms) potresti non guadagnare. Utile per operazioni > 100ms.
Non funziona in FPM/Apache, solo in Artisan e worker.
Se in parallelo viene sollevata un’eccezione, run() la rilancia. Per continuare, gestiscila dentro ciascuna closure con try/catch.

Prossimi passi

Code e job

Impara code e job per l’esecuzione asincrona in background.
Ultima modifica il 11 settembre 2026