> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Concorrenza

> Come eseguire in parallelo più operazioni con la facade Concurrency di Laravel 13 per migliorare le prestazioni.

## 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.

<Info>
  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.
</Info>

## 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:

| Driver    | Descrizione                                                                     |
| --------- | ------------------------------------------------------------------------------- |
| `process` | Predefinito. Avvia processi PHP figli. Funziona anche nelle richieste web.      |
| `fork`    | Richiede il pacchetto `spatie/fork`. Fork del processo: solo CLI ma più veloce. |
| `sync`    | Nessun parallelismo, esecuzione sequenziale. Utile per i test.                  |

## 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.

```php theme={null}
use Illuminate\Support\Facades\Concurrency;
use Illuminate\Support\Facades\DB;

[$userCount, $orderCount] = Concurrency::run([
    fn () => DB::table('users')->count(),
    fn () => DB::table('orders')->count(),
]);
```

### Scegliere il driver

Con `driver()` specifichi un driver.

```php theme={null}
$results = Concurrency::driver('fork')->run([
    fn () => fetchFromApiA(),
    fn () => fetchFromApiB(),
]);
```

Per cambiare il driver predefinito, pubblica la configurazione e aggiorna `default`.

```shell theme={null}
php artisan config:publish concurrency
```

## Driver fork

`fork` è più veloce di `process` ma disponibile solo in CLI (Artisan, queue worker).
Non funziona nelle richieste web.

Prima installa `spatie/fork`.

```shell theme={null}
composer require spatie/fork
```

<Warning>
  `fork` non funziona nelle richieste web. Usalo in comandi Artisan o worker.
</Warning>

## Senza risultati: Concurrency::defer()

Se non ti interessa il risultato e vuoi eseguire dopo l'invio della risposta HTTP, usa `defer()`.

```php theme={null}
use App\Services\Metrics;
use Illuminate\Support\Facades\Concurrency;

Concurrency::defer([
    fn () => Metrics::report('users'),
    fn () => Metrics::report('orders'),
]);
```

Le closure non vengono eseguite al momento della chiamata, ma dopo che la risposta è stata inviata.

<Tip>
  `defer()` è ideale per raccolta di analytics o cache warming: attività che non richiedono di far attendere l'utente.
</Tip>

## Esempio pratico: chiamare più API in parallelo

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

### Sequenziale (prima)

```php theme={null}
use Illuminate\Support\Facades\Http;

public function dashboard(): array
{
    $inventory = Http::get('https://api.example.com/inventory')->json();
    $sales     = Http::get('https://api.example.com/sales')->json();
    $shipping  = Http::get('https://api.example.com/shipping')->json();

    return compact('inventory', 'sales', 'shipping');
}
```

### Parallela (dopo)

```php theme={null}
use Illuminate\Support\Facades\Concurrency;
use Illuminate\Support\Facades\Http;

public function dashboard(): array
{
    [$inventory, $sales, $shipping] = Concurrency::run([
        fn () => Http::get('https://api.example.com/inventory')->json(),
        fn () => Http::get('https://api.example.com/sales')->json(),
        fn () => Http::get('https://api.example.com/shipping')->json(),
    ]);

    return compact('inventory', 'sales', 'shipping');
}
```

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

### Aggregazioni DB in parallelo

```php theme={null}
use Illuminate\Support\Facades\Concurrency;
use Illuminate\Support\Facades\DB;

public function statistics(): array
{
    [$totalUsers, $activeUsers, $totalOrders, $revenue] = Concurrency::run([
        fn () => DB::table('users')->count(),
        fn () => DB::table('users')->where('active', true)->count(),
        fn () => DB::table('orders')->count(),
        fn () => DB::table('orders')->sum('total_amount'),
    ]);

    return [
        'total_users'  => $totalUsers,
        'active_users' => $activeUsers,
        'total_orders' => $totalOrders,
        'revenue'      => $revenue,
    ];
}
```

<Tip>
  Con `process` ogni figlio apre una nuova connessione DB. Attenzione al limite di connessioni.
</Tip>

## Nei test

Usa `sync` per esecuzione sequenziale, evitando l'overhead dei processi.

```php theme={null}
use Illuminate\Support\Facades\Concurrency;

public function test_dashboard_returns_correct_data(): void
{
    Concurrency::fake(); // Passa a driver sync

    $response = $this->get('/dashboard');

    $response->assertStatus(200);
}
```

O in `.env.testing`:

```ini theme={null}
CONCURRENCY_DRIVER=sync
```

## Note

<AccordionGroup>
  <Accordion title="Limitazioni delle closure">
    Le closure vengono serializzate. Non catturare oggetti non serializzabili (connessioni DB, file handle) da fuori: costruiscili dentro la closure.

    ```php theme={null}
    // NG
    $user = User::find(1);
    Concurrency::run([
        fn () => $user->orders()->count(),
    ]);

    // OK
    $userId = 1;
    Concurrency::run([
        fn () => Order::where('user_id', $userId)->count(),
    ]);
    ```
  </Accordion>

  <Accordion title="Overhead di process">
    `process` ha un costo di avvio dei figli: per operazioni molto brevi (\< pochi ms) potresti non guadagnare. Utile per operazioni > 100ms.
  </Accordion>

  <Accordion title="Limitazioni di fork">
    Non funziona in FPM/Apache, solo in Artisan e worker.
  </Accordion>

  <Accordion title="Eccezioni">
    Se in parallelo viene sollevata un'eccezione, `run()` la rilancia. Per continuare, gestiscila dentro ciascuna closure con `try/catch`.

    ```php theme={null}
    Concurrency::run([
        function () {
            try {
                return Http::get('https://api.example.com/data')->json();
            } catch (\Exception $e) {
                return null;
            }
        },
    ]);
    ```
  </Accordion>
</AccordionGroup>

## Prossimi passi

<Card title="Code e job" icon="layer-group" href="/it/queues">
  Impara code e job per l'esecuzione asincrona in background.
</Card>


## Related topics

- [Cache](/it/cache.md)
