Redis e Doctrine in Symfony: fatti l'uno per l'altro

Un breve tutorial per mostrare la perfetta integrazione tra il database NoSQL Redis e l'ORM Doctrine in un progetto di sviluppo basato su Symfony.

Perfetta integrazione tra il database NoSQL Redis e l'ORM Doctrine in un progetto di sviluppo basato su Symfony

Redis (REmote DIctionary Server) è un engine NoSQL open source nato nel 2009 e scritto dall'italiano Salvatore Sanfilippo. I dati sono immagazzinati nella memoria RAM e ciò rende Redis molto performante per tutte le operazioni di lettura e scrittura; appare subito evidente che uno dei limiti imposti da questa struttura è la dimensione della memoria volatile. Esiste anche un meccanismo di snapshot che, periodicamente, salva i dati su disco fisso per garantirne la persistenza.

Queste caratteristiche ben si sposano con i requisiti richiesti dal motore di caching di Doctrine che si basa su 3 tipologie di cache:

  • Metadata Cache – caching delle annotazioni, del nome delle tabelle, colonne e relazioni tra entità. Evita che ad ogni richiesta vengano lette e tradotte le caratteristiche delle entità stesse
  • Query Cache – caching delle traduzioni dei comandi DQL in SQL
  • Result Cache – caching dei risultati delle query

L'uso e la configurazione di questi 3 livelli di cache consentono di migliorare le performance dell'applicazione.

SncRedisBundle, l'elemento di unione

In ambito di un'applicazione scritta in Symfony, l'unione tra Doctrine e Redis si concretizza grazie ad un bundle open source che con poche configurazioni ci permette di implementare l'integrazione delle prime due modalità di caching.

Installazione e configurazione del bundle è ben descritta nella documentazione ufficiale per cui ci limitiamo ad indicare il link da cui attingere le informazioni.
Attivare metadata e query cache è facilissimo e il guadagno in termini di performance è subito evidente. Non ci sono controindicazioni per cui è sempre utile attivare queste funzionalità in ambiente di produzione. L'aspetto che merita un approfondimento è invece la gestione del result cache.

Result cache

L'accesso ai dati della result cache permette di diminuire gli accessi alla basedati ed ottenere dei significativi miglioramenti nella gestione delle risorse. Non c'è una strategia comune con cui gestire questa cache per cui ma c'è soprattutto da far attenzione a invalidare correttamente la cache per non trattare dati non più corretti cioè, appunto, non validi. Per sfruttare il driver di Redis, sono sufficienti un paio di righe di codice. Vediamo un esempio di una semplice funzione di repository:

<?php
namespace FooBundle\Entity;

class FooRepository extends EntityRepository
{
    public function find($id)
    {
        $qb = $this->createQueryBuilder('f')
            ->andWhere('f.id = :id')->setParameter(':id', $id)
        ;
        $qb = $qb->getQuery();

        $cacheDriver = $this->getEntityManager()->getConfiguration()->getResultCacheImpl();
        $qb->setResultCacheDriver($cacheDriver)->useResultCache(true, 3600, 'redis_foo_key');

        return $qb->getOneOrNullResult();
    }
}

Quindi è sufficiente indicare al query builder quale driver da usare. Il comando setResultCacheDriver accetta 3 parametri:

  • se usare o meno i risultati in cache
  • il tempo di validità della cache espresso in secondi
  • la chiave con cui saranno identificati i dati nella cache.

Bisogna far attenzione che la chiave sia univoca per evitare collisioni. Possiamo raffinare la gestione dell'invalidazione della cache creando una classe che sta in ascolto (listener) su un particolare evento di doctrine: onFlush.

Definiamo l'ascoltatore in un file yaml di configurazione di Symfony, services.yml per esempio:

cache_invalidator.listener:
     class: FooBundle\Listener\CacheInvalidator
public: false
     tags:
          - { name: doctrine.event_listener, event: onFlush }

Creiamo poi il listener vero e proprio:

<?php
namespace FooBundle\Listener;

class CacheInvalidator
{
    /**
     * Invalida (svuota) la cache (Redis).
     */
    public function onFlush(OnFlushEventArgs $eventArgs)
    {
        $em = $eventArgs->getEntityManager();
        $uow = $em->getUnitOfWork();
        $cacheDriver = $em->getConfiguration()->getResultCacheImpl();

        $scheduledEntityChanges = array(
            'insert' => $uow->getScheduledEntityInsertions(),
            'update' => $uow->getScheduledEntityUpdates(),
            'delete' => $uow->getScheduledEntityDeletions()
        );

        foreach ($scheduledEntityChanges as $change => $entities) {
            foreach($entities as $entity) {
                if ($entity instanceof Foo) {
                    $cacheDriver->delete('redis_foo_key');
                }
            }
        }
    }
}

Durante il ciclo di vita dell'entity manager e in particolare sul flush, il listener fa uso dello Unit Of Work per andare a leggere le entità che dovranno essere aggiunte (insert/persist), aggiornate (update) o cancellate (delete).

Se tra queste c'è un'entità i cui dati sono in qualche modo coinvolti con la cache, invalideremo quest'ultima semplicemente rimuovendo la relativa chiave su Redis che corrisponde proprio a quel terzo parametro descritto prima parlando del comando setResultCacheDriver.

Richiedi Preventivo

    Acconsento al trattamento dei miei dati personali come da informativa sulla privacy di cui ho preso visione.

    Articoli recenti

    Social Commerce: vendi dai social, proteggi il tuo business

    Nel 2025 vendere online è più semplice che mai grazie all’enorme diffusione del Social Commerce, ma allo stesso tempo è diventato più rischioso se non costruisci una strategia digitale solida e sostenibile.

    Leggi di più
    Come far comparire il tuo brand su ChatGPT nel 2025: risposte semplici alle domande comuni

    Perché ChatGPT cita alcuni brand e non altri? Semplice: l'AI non ha opinioni proprie, ma si basa su informazioni pubbliche.

    Leggi di più
    Perché il mio sito non compare su Google? 5 errori che stai (forse) facendo

    Hai investito in un sito web professionale, magari anche bello da vedere… ma i contatti non arrivano, le vendite sono ferme e la tua presenza online sembra un po’ troppo silenziosa. Ti suona familiare?

    Leggi di più

    © 2024 WEB AGENCY MILANO FUTURAWEB SRL – VIA GIUSEPPE FRUA, 19 – 20146 MILANO / P.I. 03585460961 / REA 1686213 / CS 10000€ IV / PRIVACY

    crossmenuchevron-down
    linkedin facebook pinterest youtube rss twitter instagram facebook-blank rss-blank linkedin-blank pinterest youtube twitter instagram