La cache è uno degli strumenti più seducenti nell'ingegneria del software. Con poco sforzo trasformi un'operazione lenta in una risposta istantanea. Il sistema diventa più veloce, l'infrastruttura respira, l'utente sorride. Sembra magia.
Ed è proprio perché sembra una magia che la cache provoca così tanti danni. Risolve il problema delle prestazioni così facilmente che le persone spargono la cache ovunque, senza rendersi conto che stanno scambiando un problema visibile, lentezza, con un problema invisibile, dati errati. E i dati errati sono molto peggio dei dati lenti.
Questa è una guida rapida per utilizzare correttamente la cache. L'idea non è esaurire l'argomento, ma piuttosto darvi i criteri che separano la cache che aiuta dalla cache che diventa una trappola. Come dice il classico scherzo informatico, ci sono solo due problemi difficili: l'invalidazione della cache, la denominazione delle cose e l'errore uno più uno.
Cosa fa la cache, in una frase
La memorizzazione nella cache consente di salvare il risultato di un'operazione costosa per riutilizzarlo, invece di ripetere l'operazione ogni volta. Lo calcoli una volta, lo salvi e la volta successiva fornisci la risposta salvata.
L'operazione "costosa" potrebbe essere una pesante interrogazione del database, una chiamata a un servizio esterno, un calcolo complesso o il rendering di una pagina. Il guadagno deriva dal non ripetere il lavoro. Quando lo stesso risultato viene richiesto molte volte e cambia poco, la memorizzazione nella cache è quasi sempre una buona idea.
La parola chiave è "cambia poco". È qui che risiede tutta la complessità, ed è qui che la maggior parte delle persone sbaglia.
Buona pratica: memorizzare molto nella cache di ciò che si legge e cambia poco
Il candidato cache ideale ha due caratteristiche: vi si accede frequentemente e cambia raramente. Pensa ad un elenco di categorie di prodotti, alla configurazione di un sistema, al profilo pubblico di un utente. Questo tipo di dati viene letto continuamente e aggiornato di tanto in tanto, cache perfetta.
Il cattivo candidato alla cache è l'opposto: dati che cambiano in ogni istante o la cui precisione è fondamentale in tempo reale. Il saldo di un conto bancario, lo stock disponibile al momento dell'acquisto, il prezzo in trattativa attiva, il caching possono restituire all'utente un numero non più vero, con conseguenze reali.
La domanda pratica prima di memorizzare nella cache qualsiasi cosa: cosa succede se l'utente vede un valore obsoleto di pochi secondi o minuti? Se la risposta è "niente di grave", memorizza nella cache. Se la risposta è "un problema serio", pensaci due volte.
Il vero problema: l'invalidazione
Mettere i dati nella cache è banale. La parte difficile è sapere quando rimuovere o aggiornare. Questo è il problema dell'invalidazione ed è da qui che nascono quasi tutti i bug della cache.
Esistono due strategie di base ed entrambe hanno il loro posto. Il primo è la scadenza: i dati rimangono nella cache per un periodo definito e poi vengono scartati. Semplice, robusto e sufficiente per la maggior parte dei casi. Accetti che i dati possano non essere aggiornati per, diciamo, cinque minuti e vai avanti con la tua vita.
Il secondo è l'invalidazione degli eventi: quando i dati cambiano, rimuovi o aggiorni attivamente la versione memorizzata nella cache. È più preciso, ma più fragile, richiede che ogni modifica nei dati venga ricordata di notificare la cache e tutto ciò che serve è un percorso dimenticato affinché l'utente possa vedere le vecchie informazioni a tempo indeterminato.
Migliore pratica per una guida rapida: preferire la scadenza temporizzata ogni volta che la tolleranza lo consente. È più semplice, più resistente agli errori umani ed evita la classe di bug in cui la cache "si dimentica" di aggiornarsi. Riservare l'invalidamento degli eventi per quando l'accuratezza giustifica veramente la complessità.
Buona pratica: definire cosa succede quando la cache fallisce
La cache è un livello aggiuntivo e i livelli aggiuntivi falliscono. Il server della cache potrebbe non funzionare, diventare non disponibile o rallentare. La domanda a cui molti dimenticano di rispondere: cosa si ferma allora il sistema?
Una memorizzazione nella cache ben eseguita è un'ottimizzazione, non una dipendenza. Se la cache scompare, l'applicazione deve continuare a funzionare, forse più lentamente, ma funzionante, cercando i dati dalla fonte originale. Quando l'intero sistema va in crash perché la cache si blocca, non hai un'ottimizzazione; ha un singolo punto di errore mascherato da miglioramento delle prestazioni.
C'è anche un dettaglio insidioso: quando la cache si svuota contemporaneamente, tutte le richieste raggiungono contemporaneamente la fonte originale e il sovraccarico può distruggere proprio ciò che la cache proteggeva. È un effetto noto e vale la pena progettare il ripristino tenendolo presente, in modo che il sistema riscaldi nuovamente la cache senza annegare.
Riflessione critica: la cache nasconde il problema sbagliato
Esiste un uso del caching che è sia tecnicamente corretto che strategicamente pigro: il caching per nascondere una query mal fatta. La query è lenta perché è scritta in modo inadeguato o il database non è modellato in modo corretto e, invece di correggere la causa, viene generata la cache. Funziona finché la cache non scade, finché non viene visualizzato il caso non memorizzabile nella cache, finché il problema non si risolve.
La cache dovrebbe accelerare ciò che è già efficiente, non compensare ciò che è inefficiente. Quando ti ritrovi a utilizzare la cache per rendere sopportabile qualcosa che dovrebbe essere risolto, vale la pena fermarsi e esaminare la causa. La cache, in questo caso, rinvia un debito, non lo paga.
C’è anche il costo cognitivo. Ogni livello della cache è un'altra cosa in cui i dati potrebbero non essere aggiornati, un posto in più su cui indagare quando qualcosa è strano. "Perché questo utente vede vecchie informazioni?" è una delle domande più frustranti su cui eseguire il debug, proprio perché la cache è invisibile finché non si verifica un problema. Troppa cache trasforma un sistema semplice in un puzzle di versioni. Usalo con parsimonia, documenta dove ti trovi e preferisci meno livelli ben compresi a molti livelli misteriosi.
Ciò che resta
La cache è uno strumento potente ed è altrettanto spesso utilizzata in modo improprio. Usato bene lascia sistemi veloci ed economici. Viene utilizzato in modo improprio, fornisce informazioni sbagliate, nasconde problemi reali e crea bug difficili da monitorare.
Le buone pratiche stanno in poche righe: mettere in cache ciò che si legge molto e cambiare poco; preferire la scadenza temporizzata all'invalidazione manuale; garantire che il sistema sopravviva senza cache; e non utilizzare mai la memorizzazione nella cache per nascondere un problema che dovrebbe essere risolto. Il resto è messa a punto.
Se stai riscontrando problemi di prestazioni e consideri la memorizzazione nella cache come soluzione, vale prima la pena capire se il collo di bottiglia è effettivamente dovuto a letture ripetute o a qualcosa di più profondo. Sul blog sono presenti altri testi su backend, prestazioni e architettura che approfondiscono queste scelte.
Leggi anche
- Backend per le applicazioni: buone pratiche per piccoli team che non possono commettere errori
- Cache nelle applicazioni: buone pratiche e principi fondamentali
- Cache nelle applicazioni: buone pratiche e passaggi essenziali
- Cache nelle applicazioni
- Architettura dell'applicazione: Guida completa ai sistemi scalabili
- Scalabilità dell'applicazione: Guida tecnica completa
