Qualche settimana fa abbiamo visto cosa sono i service worker e come utilizzarli per creare progressive web application ed ottenere elevate prestazioni in termini di tempo di caricamento della pagina.
I più attenti tra di voi si saranno accorti che, a prescindere dalla strategia di caching adottata, non esiste un time to live degli elementi memorizzati in cache, che siano risorse statiche o pagine HTML. Una volta in cache, pertanto, l’elemento ci rimane fino a quando l’intera cache non viene invalidata modificando il service worker in sé.
Sicuramente una noia non da poco in quanto quello che vogliamo ottenere è un’invalidazione degli elementi che sul server hanno subito delle modifiche e lasciare inalterati quelli che non lo sono. Pensate se grazie al service worker avete memorizzato in cache 50 pagine e per via di una che ha subito una modifica bisogna svuotarla tutta e ricaricarla completamente. Uno spreco di risorse e di tempo.
Per risolvere questo inconveniente ho sviluppato tre tecniche di caching: una comandata dal server, che consente di effettuare il refresh di pagine e risorse solo se il server lo impone, l’altra da un time to live, simulando pertanto la browser cache standard, l’ultima una fusione delle due.
Indice dei contenuti
Questa è una tecnica completamente nuova, by SpeedyWordpress. L’obiettivo è creare un service worker in grado di effettuare il caching di tutte le risorse via via che il sito viene navigato, impostando per ogni risorsa un Time To Live (TTL) in funzione della tipologia: risorsa statica o una pagina HTML.
Come già detto la cache con cui interagisce il service worker non funziona come la classica browser cache e non contiene un TTL, per cui occorre realizzare un workaround: creare un campo nello header HTTP della risorsa memorizzata contenente il timestamp dell’inserimento in cache. Tale campo è stato denominato sw-cache-timestamp.
In questo modo, ogni qual volta la risorsa viene prelevata dalla cache sarà possibile effettuare un confronto tra il timestamp e il valore di TTL impostato sul service worker, ad esempio 7200 secondi. Se la risorsa risulterà essere troppo vecchia verrà eliminata dalla cache, scaricata dal server e inserita in cache nuovamente.
Per rendere tutto più dinamico, il valore di timestamp di ogni risorsa può essere specificato pure dall’applicazione lato server semplicemente impostando nello header HTTP della risorsa il campo sw-cache-timestamp prima di essere scaricata dal client. Il service worker a quel punto memorizzerà in cache la risorsa con il timestamp impostato dall’applicazione, per un controllo completo e dinamico della nostra cache.
Vediamo insieme il funzionamento di questa nuova tipologia di service worker. Questo è il service worker utilizzato per la dimostrazione e contiene un TTL di 7200 secondi sia per risorse statiche che per pagine HTML. È possibile inoltre impostare a true l’opzione di debug per vedere nella console tutta una serie di messaggi sul funzionamento.
Iniziamo! Alla prima visita del sito il service worker viene installato e la cache risulta naturalmente essere vuota. Non esiste pre-caching in questa tecnica.
Al momento in cui inizia la navigazione, il service worker inizia a popolare la cache. È possibile vederlo in tempo reale dalla console se è stata attivata la modalità debug:
Questi sono i tempi di risposta delle risorse e della pagina HTML durante il popolamento della cache:
Abbiamo una media di 300 ms di tempo di download a risorsa. Analizzando la cache è possibile notare, risorsa per risorsa, il valore di timestamp impostato dal service worker (o dall’applicazione, se è stata configurata):
A questo punto non rimane che verificare il tempo di download delle stesse risorse ma dopo il loro inserimento in cache:
Il tempo medio di download di ogni risorsa si è abbassato da 300 ms a 35 ms. Il DOMContentLoaded è diminuito da 1.26 secondi a 569 millisecondi ed un ulteriore secondo di caricamento è stato risparmiato sul tempo di Load della pagina.
Si tratta di un service worker interconnesso con l’applicazione web, sia essa WordPress, Joomla o qualsiasi altra tipologia di sito. Il compito del service worker sarà effettuare un pre-caching delle pagine in funzione delle regole dettate dall’applicazione web.
In poche parole, l’applicazione genera un array bidimensionale formato da coppie URL-hash, dove l’hash è il message digest del contenuto della URL. Esempio:
var precacheConfig = [
["/", "3cb4f0"],
["/blog/", "c5a951"],
["/sample-page/", "ff3ds"]
]
Questo array viene scansionato dal service worker durante l’evento install, ovvero nella fase di installazione del service worker. In particolare viene controllato se esiste in cache la relativa URL con un parametro corrispondente al valore dell’hash.
A questo punto le casistiche sono due:
Questa configurazione consente di effettuare un pre-caching delle pagine più importanti del sito ed invalidare la cache solo per quelle che sono state effettivamente modificate, la stessa logica che segue sw-precache.
Questo è il service worker utilizzato per questa dimostrazione. Al momento della prima visita al sito, il service worker viene installato e subito effettua il pre-caching delle 3 risorse configurate, ovvero:
Com’è possibile notare dall’immagine della cache, tutte e tre le risorse hanno infatti lo stesso Time Cached. Analizzando nel dettaglio una delle risorse è possibile notare il famoso parametro con hash, _sw-precache:
A questo punto se si prova a visitare una delle pagine in cache, il download risulterà rapidissimo:
Il tempo di download della pagina è infatti di appena 5 millisecondi. A questo punto è possibile simulare una modifica della pagina cambiando il valore dell’hash nell’array precacheConfig all’interno del service worker.
Il browser noterà che il service worker è stato modificato e ne forzerà una nuova installazione. Fatto questo non rimane che verificare che sia stata aggiornata la sola pagina modificata:
Il Time Cached è rimasto invariato per le altre pagine a parte per quella del blog che è stata scaricata nuovamente.
Per funzionare dinamicamente, questa tecnica prevede la scrittura di uno script PHP lato server che ogni qual volta viene effettuata una modifica di una delle pagine da pre-cachare va a modificare l’hash presente all’interno dell’array precacheConfig del service worker.
Questa tecnica è la fusione tra la prima è la seconda. Banalissimamente al momento del fetch se la risorsa non è una di quelle inserite in cache in fase di pre-caching, viene gestita con la politica del TTL dinamico.
Questo è il service worker che implementa la tecnica.