ProcessWire
Gestire un sito enorme con ProcessWire

Gestire un sito da milioni di pagine con ProcessWire

Frank Vessia
12 ott 2020
Tempo di lettura: 5 minuti, 45 secondi

Gestire milioni di pagine in ProcessWire mantenendo alte le prestazioni del sito anche con traffico elevato.


Nel corso degli anni mi è capitato di dover gestire siti di grosse dimensioni con centinaia di migliaiai di pagine e milioni di dati, ho così tenuto una lista di alcuni steps diciamo obbligatori che eseguo quando ora mi trovo a dover affrontare un progetto di questo tipo, passaggi che aiutano a migliorare le prestazioni e a gestire dati importanti.

ProcessWire è un framework eccezionale, non mi stancherò mai di ripeterlo, il bello è che man mano lo usi scopri sempre qualcosa di nuovo ed interessante che magari risolve una nuova necessità, sono così tante le chicche di cui dispone che elencarle tutte sarebbe impossibile, per questo spesso mi segno alcuni passaggi, impostazioni o pratiche utili per diverse necessità in modo da ricordare (laddove non dovessi farlo a memoria) quale è l'approccio migliore per ogni progetto.

Come anticipato, questa volta parliamo di dati importanti e numerosi, dati che in altri sistemi metterebbeo a dura prova mysql con relativi rallentamenti. Vediamo come dobbiamo comportanrci con ProcessWire per avere un sistema sempre pronto, veloce e senza colli di bottiglia.

Extended file mapping

Per chi non lo sapesse, ProcessWire crea una cartella sul server per ogni pagina del sito con l'id della pagina stessa, questo serve al framework per ospitare eventuali assets relativi alla pagina, come immagini o file allegati. Se il sito che andremo a creare avrà un numero enorme di pagine questo potrebbe causare un problema su server che hanno l'impostazione di "hard limit" sul numero massimo di cartelle per livello, per esempio i sistemi ext2 hanno il limite a 30.000. Per ovviare a questo problema basta impostare nel file config questa variabile su true:

$config->pagefileExtendedPaths = true;

Questa variabile pagefileExtendedPaths istruirà il ProcessWire ad utilizzare un sistema più complesso nella creazione delle cartelle che non sarà semplicemente incrementale in una singola folder ma "spezzerà" l'id di una pagina e creaerà cartelle annidate tra loro per avere un numero massimo di cartelle per livello non superiore a 2000. Questo risolve definitivamente ogni tipo di problema server, da notare che in sistemi già esistenti, l'abilitare questa funzione influirà sulla creazione di cartelle successive.

Autojoin

Processwire AutojoinL'autojoin è una proprietà a livello campo, utilissima per ottimizzare le query e il carico del server. Normalmente al caricamento di una pagina ProcessWire non carica in memoria tutti i campi di un determinato template i quali vengono richiamati solo nel momento in cui vogliamo visualizzare un dato o nel caso in cui tramite API lo andiamo a richiamare.

Impostando un campo su Autojoin questo verrà precaricato in memoria insieme alla pagina con una sola query, utile quindi in tutte quelle situazioni in cui un campo viene sempre utilizzato evitando che venga effettuata una query ulteriore. Questa proprietà si imposta sul campo, nel tab "Advanced".

Facciamo un esempio, mettimao il caso di avere un sito di news e il template "articolo" ha i seguenti campi:

  • titolo
  • descrizione
  • data
  • testo
  • autore

Sappiamo per certo che il titolo e la descrizione sono dei campi che andremo sempre ad utilizzare, in home page, nelle pagine delle categorie e nella pagina dell'articolo stesso per cui questi due campi è bene che abbiano la proprietà autojoin selezionata (da notare che il titolo "title" è un campo predefinito di ProcessWire ed ha l'autojoin impostato di base).

Limit per i count

Ogni qualvolta abbiamo la necessità di calcolare un totale di pagine è sempre consigliato inserire un limit nel selector per evitare di caricare tutti gli elementi di quel selector nella query.

// codice non consigliato
$books = $pages->find("template=book,category=thriller");
$books_total = $books->getTotal();
//codice ottimizzato
$books_total = $pages->find("template=book,category=thriller,limit=2")->getTotal();

Inserirendo il limit maggiore di 1, caricheremo solo 2 elementi nella query e il getTotal ci restituirà il count di tutti gli elementi del selector senza considerare il limit impostato. Questo è un grande incremento di prestazioni specialmente se consideriamo situazioni in cui dobbiamo visualizzare molti totali e quindi molti selector. Da notare però che un selector di questo tipo non può essere utilizzato successivamente per ciclare gli elementi poichè il limit avrà effetto e l'array di pagine avrà soltanto il numero specificato nel limit.

findMany()

Questa funzione disponibile dalla versione 3.0.19 di ProcessWire è stata creata appunto per gestire query enormi. É simile al classico $pages->find() ma con l'opzione di "lazy loading" per prevenire errori di memoria esaurita se la query dovesse cercare un numero enorme di pagine. Con findMany possiamo tranquillamente caricare centinaia di migliaia di pagine senza preoccupazioni, il sistema caricherà le pagine in blocchi man mano procederemo a ciclare il PageArray. Questo modo di utilizzare un selector è utilissimo in caso di calcoli su determinati campi, facciamo un esempio come riporta la documentazione:

// Calcolo del costo
$totalCost = 0;
$items = $pages->findMany("template=foo"); // 50000 pagine
foreach($items as $item) {
  $totalCost += $item->cost;
}
echo "Il costo totale è: $totalCost";

In questa situazione non potevamo usare il limit perchè ci serviva iterare tutti gli elementi, il calcolo in fatti viene fatto su campo "cost" di ogni pagina.

WireCache

ProcessWire dispone di un ottimo sistema di caching che non tutti però utilizzano o di cui ne sono a conoscenza. Oltre alla cache delle pagine impostabile a livello di template dal pannello "Cache" possiamo utilizzare un sistema di caching a livello API per markup, stringhe, array o PageArray.

Vediamo come funziona, mettiamo il caso in cui debba visualizzare in una pagina un blocco in cui mostro gli ultimi 10 commenti degli utenti. Poichè nel mio sito ho diversi dati da visualizzare voglio mettere in cache questo blocco per evitare di dover effettuare delle query ad ogni singolo caricamento della pagina, utilizzo quindi la variabile $cache per costruitre il mio blocco in questo modo:

$commentsBlock = $cache->get("myComments");
if( !$commentsBlock ) {
    $comments = $pages->find("template=comment,sort=-created,limit=10");
    $commentsBlock = '';
    foreach($comments as $c){
        $commentsBlock .= "<div>{$c->title}</div>";
    }
    $cache->save('myComments', $commentsBlock, 3600 );
}
echo $commentsBlock ; 

Il codice non fa altro che richiamare un oggetto dalla cache "myComments", se non esiste lo valorizza e lo salva, altrimenti lo visualizza direttamente dalla cache. In questo caso il mio blocco di commenti verrà aggiornato sul mio sito ogni ora, cioè una query ogni ora al posto di una query ad ogni caricamento di pagina, bellisismo vero?

Questo è un esempio molto base, nella realtà il markup di un blocco di commenti conterrebbe molto altro, ma rende benissimo l'idea del concetto di cache. Da notare la funzione save() che contiene 3 elementi, il nome dell'oggetto in cache, il contenuto della cache e la durata in secondi.

Conclusioni


Queste sono le principali pratiche e impostazioni da considerare sempre nella creazione di un sito o applicazione web, obbligatorie se parliamo di siti con un numero elevatisismo di pagine e anche di traffico. La community di ProcessWire è molto attiva e ci sono anche tanti moduli creati da sviluppatori per ottimizzare le query e altro, come ad esempio RockFinder