Un’importazione che richiede ore, un report che interroga diverse fonti o un’attività che coordina integrazioni può continuare a consumare risorse molto tempo dopo aver perso la sua utilità. Senza limiti espliciti, è anche difficile capire se sta lavorando, è bloccata o è incompleta. Progettare limiti di tempo nei processi PHP di lunga durata richiede di definire quanto può durare ogni fase, come interrompere il lavoro e quale stato conservare se l’esecuzione viene interrotta.
L’obiettivo non è imporre un unico valore all’intero processo. È stabilire un budget coerente dall’inizio alla fine, trasmetterlo alle operazioni dipendenti e fare in modo che lo stato finale rispecchi quanto è accaduto. Un timeout, una richiesta di cancellazione e un’interruzione forzata sono situazioni diverse e vanno trattate come tali.
Perché serve un limite esplicito

Un processo senza scadenza può trattenere indefinitamente memoria, connessioni e capacità di esecuzione. Può anche lasciare un’attività contrassegnata come attiva anche se non avanza più. In un ambiente con worker, il problema si amplifica: un’esecuzione bloccata occupa un worker e può ritardare i lavori successivi.
I limiti di PHP o del server non sostituiscono una policy applicativa. Per esempio, max_execution_time può limitare l’esecuzione di uno script, ma non rappresenta necessariamente il tempo totale che l’organizzazione è disposta ad aspettare. Inoltre, i limiti del web server, di PHP-FPM, del queue manager, del client HTTP e del database possono agire in modo indipendente. Se sono diversi, un livello può terminare il lavoro senza che un altro riesca a salvare lo stato corrispondente.
Per prima cosa, individua dove viene eseguito il processo e quali componenti possono interromperlo. Un’attività da console, un queue worker e una richiesta web hanno meccanismi e conseguenze differenti. Il limite applicativo deve convivere con questi controlli esterni, senza dipendere dal fatto che uno di essi gestisca da solo la terminazione.
Distingui timeout, deadline e cancellazione
- Timeout di un’operazione: tempo massimo di attesa per una singola chiamata, come una query o una richiesta HTTP.
- Deadline globale: istante limite per l’intero processo. Le fasi condividono il tempo rimanente, invece di ricevere ciascuna la durata totale.
- Richiesta di cancellazione: decisione esplicita di smettere di lavorare, per esempio perché un utente ha richiesto la cancellazione dell’attività o il sistema l’ha rimossa da una coda.
Un timeout indica che un’operazione non si è conclusa entro il proprio budget. Non implica di per sé che l’intero processo debba interrompersi: dipende dalla fase e dalle regole del prodotto. La cancellazione, invece, è un segnale di controllo che il processo deve verificare e gestire quando può farlo in sicurezza. Nessuna delle due garantisce che un sistema operativo o un supervisor interrompa l’esecuzione in un punto preciso; una terminazione forzata può verificarsi tra due controlli.
Rappresenta la deadline come un istante assoluto o come tempo rimanente calcolato a partire da un orologio monotono. Prima di avviare una fase, verifica che ci sia ancora tempo a disposizione. Il calcolo del tempo rimanente evita che una sequenza di operazioni con timeout individuali possa consumare molto più del limite globale. In PHP, incapsula il calcolo e il controllo della cancellazione in un oggetto o servizio condiviso, invece di duplicare confronti sparsi nel codice.
Ripartisci il budget tra fasi e dipendenze
Scomponi il processo in fasi osservabili: lettura, trasformazione, scrittura e coordinamento esterno, per esempio. Definisci quanto tempo può consumare ciascuna senza superare la deadline globale. Non è necessario che tutte le scadenze siano identiche: una fase con lavoro locale circoscritto può richiedere un limite diverso da una chiamata a un servizio remoto.
Quando chiami una dipendenza, usa il valore minore tra il suo timeout operativo e il tempo rimanente del processo. Configura separatamente i limiti di connessione e di lettura quando la libreria lo consente. Una connessione rapida non impedisce alla risposta di tardare troppo, e un timeout di lettura non include necessariamente tutto il tempo precedente. Applica lo stesso principio alle query SQL, alle operazioni di storage e ai processi secondari, utilizzando i controlli offerti da ciascun client.
Se un’operazione non supporta la cancellazione, non presentarla come immediatamente interrompibile. Puoi limitare il tempo di attesa, isolarla in un’unità di lavoro separata oppure progettare il flusso in modo da verificare la cancellazione prima e dopo. Per i processi secondari, conserva l’identificatore necessario per richiederne la terminazione e verifica che il processo sia effettivamente terminato: inviare un segnale non equivale a confermarne l’uscita.
Interrompi il lavoro e lascia uno stato comprensibile
Quando la deadline scade o arriva una richiesta di cancellazione, evita di avviare nuove fasi. Nei punti sicuri, completa le azioni locali indispensabili per registrare il risultato e rilascia le risorse gestibili, come cursori o file temporanei. Il blocco di pulizia non dovrebbe tentare di completare il lavoro in sospeso: deve avere a sua volta una durata limitata e non nascondere la causa dell’interruzione.
Salva lo stato dell’attività in una posizione persistente, associandolo a un identificatore stabile. Come minimo, registra stati distinguibili, per esempio running, completed, cancelled e timed_out, oltre alla fase corrente e ai timestamp rilevanti. Aggiungi un motivo leggibile dagli operatori, senza salvare dati sensibili non necessari. Aggiorna lo stato in modo atomico quando possibile e considera che il processo potrebbe terminare prima di completare l’aggiornamento.
Il risultato mostrato a chi ha avviato l’attività deve distinguere «cancellata» da «fallita per timeout». Se dopo un’interruzione improvvisa non è ancora possibile confermare lo stato, mostralo come sconosciuto o in attesa di verifica, non come completato. Le metriche e i log strutturati possono includere durata, fase e tipo di interruzione; evita di registrare segreti, contenuti privati o stack trace senza controlli di accesso.
Decidi se gli effetti parziali possono essere ripresi
L’interruzione può verificarsi dopo aver scritto i dati e prima di registrare il completamento della fase. Perciò, decidere se è possibile riprendere non consiste soltanto nel salvare il numero della fase: bisogna capire quali effetti potrebbero essersi verificati. Per le operazioni locali, una transazione può raggruppare modifiche correlate, se la sua durata e il motore lo consentono. Non mantenere una transazione aperta durante una lunga chiamata esterna.
Per i lavori batch, registra le unità completate con una chiave stabile e definisci quando un’unità è effettivamente confermata. Se al riavvio una scrittura viene ripetuta, la progettazione deve impedire i duplicati o riconoscere che l’effetto esiste già. Un’integrazione esterna che non offre la cancellazione potrebbe aver accettato una richiesta anche se il client ha smesso di attendere; lo stato deve riflettere questa incertezza, senza presumere che non ci siano stati effetti.
Se non è sicuro continuare dall’ultimo punto salvato, contrassegna l’attività per una revisione o per un recupero specifico. Riprendere automaticamente da un checkpoint impreciso può produrre un risultato errato. Documenta quali fasi sono ripetibili, quali richiedono una verifica e quali condizioni consentono di dichiarare concluso il processo.
Verifica i limiti e le interruzioni
I test devono verificare sia l’interruzione sia lo stato finale, non solo che sia stata lanciata un’eccezione. Usa dipendenze controllabili per simulare una chiamata lenta, una query che supera la propria scadenza e una risposta che arriva proprio al limite. Verifica che la durata di una fase non consenta di ignorare la deadline globale.
Verifica una cancellazione tra le fasi e durante un’operazione che può essere fermata soltanto quando restituisce il controllo. Simula anche un’interruzione improvvisa in punti rilevanti: dopo un effetto esterno, prima di salvare lo stato e durante la pulizia. Dopo il riavvio del worker, verifica che lo stato sia comprensibile e che la policy di ripresa non duplichi né ometta lavoro. Questi test devono usare dati isolati e non dipendere da servizi esterni reali.
Checklist per un processo esistente

- Individua i limiti di PHP, del server, del worker e di ogni client utilizzato.
- Definisci una deadline globale e budget per fase che, nel complesso, non la superino.
- Propaga il tempo rimanente a HTTP, database e altri processi quando possibile.
- Verifica la cancellazione nei punti sicuri ed evita di avviare nuovo lavoro dopo averla ricevuta.
- Salva stati distinguibili, la fase corrente e la causa della conclusione.
- Descrivi quali effetti parziali possono rimanere e quali condizioni consentono la ripresa.
- Verifica ritardi, cancellazioni, interruzioni improvvise e lo stato osservato dopo il riavvio.
Una progettazione solida non promette che ogni operazione possa essere interrotta istantaneamente. Rende esplicito il tempo disponibile, limita l’attesa per ogni dipendenza e conserva informazioni sufficienti per interpretare un’interruzione. Questa combinazione permette a un processo di lunga durata di smettere di essere un’esecuzione opaca e di diventare un’unità operabile e verificabile.



