L’anno scorso mi sono trovato di fronte ad un problema alquanto scomodo: ristrutturare gli URL dei contenuti presenti nel sito di un cliente per migliorarne la leggibilità e l’indicizzazione nei motori di ricerca. Tralasciando tutte le questioni inerenti ai redirect 301 da gestire, voglio concentrare questo post sulle performance raggiunte per aggiornare più di 200.000 path alias.
Il mio primo approccio è stato quello di utilizzare il “gancio” hook_update_N()
come ho descritto nell’articolo di qualche settimana fa.
La procedura faceva correttamente il suo dovere, ma con tempi di esecuzione
improponibili al cliente, poiché tenere offline per due ore e mezza un sito che
fa circa 6,5K di visite al giorno non lo aiuta a mantenere una buona reputazione nel web.
La prima cosa che mi è balzata in mente è stata quella di suddividere l’intera mole di dati in porzioni più piccole da processare in modo concorrente: il cosiddetto multithreading. L’ articolo di John Ennew (Techical Lead in Deeson) è stato provvidenziale, poiché mi è bastato personalizzare il suo drush multithread manager per raggiungere il mio scopo. Per vostra conoscenza, potete trovare il codice completo scritto da John su GitHub.
Vediamo allora come implementare l’aggiornamento dei path alias con un comando Drush multithreading.
Creare il comando Drush
La prima cosa che ho fatto è stata quella di creare il mio comando Drush. Per essere più precisi ho creato due comandi:
-
update-alias
, che esegue l'aggiornamento del path alias per l'insieme di nodi che gli verrà passato dal gestore del multithread. -
mt-command
, che eseguirà il comandoupdate-alias
in modalità multithread;
Vediamo in dettaglio il contenuto delle funzioni eseguite da questi due comandi.
A proposito, per associare un conmando ad una funzione basta il nome di quest'ultima
sia formato dal nome del comando sostituendo il 'meno' (-, hyphen) con il
'trattino basso' (_, underscore) ed aggiungere come prefisso drush_
.
Quindi se il comando è mt-command
la funzione sarà drush_mt_command
.
Aggiornare i path alias
Partiamo dall funzione più semplice, quella che esegue l'aggiornamento degli URL.
Per prima cosa recuperiamo l'insieme di nodi da aggiornare. La cardinalità
di questo insieme è definita dal paramentro $limit
, mentre
il punto di partenza da cui iniziare l'aggiornamento lo troviamo nel parametro
$offset
. Il parametro $thread
infine contiene
l'identificativo del thread in esecuzione su questo insieme di dati.
Poi aggiorneremo l'URL utilizzando la funzione pathauto_node_update_alias()
che troviamo all'interno del modulo Pathauto.
Eseguire processi concorrenti
Quello che dobbiamo fare ora è riuscire ad eseguire contemporaneamente la funzione descritta qui sopra su porzioni di dati differenti. Per farlo abbiamo bisogno di definire le seguenti cinque funzioni:
-
drush_mt_command()
, implementa il comandomt-command
. Non fa altro che avviare la gestione multithread per l'aggiornamento di tutti i nodi; -
_mt_command_setup()
, costruisce il comando Drush da eseguire per ogni singolo thread. Nel nostro caso il comando saràupdate-alias
; -
_mt_command_teardown()
, se abbiamo bisogno di eseguire alcuni comandi al termine dell'esecuzione di un thread, li possiamo inserire qui; -
_mt_monitor_process()
, chiude in modo sicuro il processo quando ha terminato la sua esecuzione; -
drush_thread_manager()
, gestisce l'esecuzione di tutti i processi fino a completare l'aggiornamento del path alias per tutti i nodi.
Nel mio caso le funzioni che ho personalizzato sono le prime due e le vedremo
in dettagli qui di seguito. Mentre le altre le ho praticamente copiate
e lasciate invariate come potete trovare nel
repository di GitHub. La funzione _mt_command_teardown()
l'ho lasciata vuota visto che non c'è bisogno di utilizzarla.
Setup di ogni singolo job
Il compito di ogni singolo job è quello di aggiornare il path alias
di un'insieme definito di nodi. Grazie ai parametri $limit
e
$offset
posso identificare la prozione di nodi su cui lavorare.
Il paramentro $thread_id
l'ho aggiunto solo per poterlo stampare
come log. La funzione _mt_command_setup
alla fine restiuisce
una stringa contentente la riga di comando drush che verrà eseguita dal
gestore dei processi.
Il gestore dei processi concorrenti
Chi è che inizializza e avvia il gestore di processi? Allora, ricollegandomi
a quanto stavo dicendo qualche riga fa, questo compito è svolto dalla funzione
drush_mt_command()
. Per eseguire la funzione lanciamo il comando
$ drush mt-command [limit] [batch_size] [threads]
dove:
-
limit
, è il numero totale dei nodi che vogliamo aggiornare; -
batch_size
, la quantità di nodi da elaborare per ogni job; -
threads
, il numero di processi che possono essere in esecuzione contemporaneamente.
Se vogliamo elaborare tutti i nodi presenti nel nostro database (nel mio caso ho deciso di aggiornare solo i contenuti di tipo 'article' e 'page'),
allora dobbiamo impostare a 0 il paramentro limit
.
A questo punto è tutto pronto per avviare il gestore dei processi.
Conclusioni
Se dovete eseguire applicare qualche modifica a una grossa mole di dati del vostrp sito web in Drupal, penso che questo instroduzione alla programmazione multi-thread utilizzando Drush faccia al caso vostro.
Nel mio repository GitHub potete trovare il codice completo. Sono certo che si può far ancora meglio per velocizzare la procedura che ho descritto qui sopra, quindi se avete suggerimenti da darmi contattatemi, sarò felice di discuterne assieme.