Article rédigé par Didier, Tech Lead à L’Agence Dn’D

Souvent négligée, parfois méconnue, voire accusée dès qu’un bug apparaît, la gestion du cache dans n’importe quel logiciel est un élément essentiel à considérer pour optimiser les performances et l’utilisation d’une application.

En termes simples, un cache agit comme une mémoire intermédiaire qui stocke des données en vue d’un accès ultérieur, réduisant ainsi le temps nécessaire pour y accéder. Le cache se positionne comme une couche invisible entre l’utilisateur et la source des données.

Les technologies utilisées pour mettre en place le cache sont variées, allant du cache sur fichier à des architectures plus complexes utilisant une base de données en mémoire via des services tels que Redis ou Memcache. La différence principale réside dans la « localisation physique » du cache, puisqu’en fonction de la technologie utilisée, il peut être enregistré sur le disque dur ou en mémoire vive.

L’utilisation du disque dur introduit de la latence, car les données écrites sur ce support sont destinées à être conservées. De plus, les bases de données appliquent souvent la compression et le cryptage, augmentant ainsi la latence lors de la lecture et de l’écriture des données. Pour surmonter ces défis, l’alternative de la base de données en mémoire est privilégiée, permettant un stockage et une récupération rapides des données depuis la RAM plutôt que depuis un disque. L’utilisation de la mémoire vive (RAM) vise précisément à ne conserver les données que pendant une durée déterminée, répondant ainsi parfaitement à la définition même d’un cache.

Norme PSR (PHP Standard Recommandation)

Dans Symfony et les autres frameworks PHP modernes, la gestion du cache suit la norme PSR-6. Elle offre la possibilité à chaque framework de respecter un consensus sur la mise en œuvre et l’interaction avec un cache, procurant ainsi une flexibilité accrue et une facilité d’adaptation à divers outils de cache.

La norme PSR-6 repose sur deux interfaces fondamentales :

  • CacheItemInterface : représente un objet que l’on souhaite placer en cache
  • isHit() : vérifie si la recherche de l’élément en cache a été fructueuse.
  • set($value) : définit la valeur représentée par cet élément en cache.
  • expiresAt($expiration) : fixe le moment d’expiration de cet élément en cache.
  • expiresAfter($time) : fixe la durée de vie de cet élément en cache.
  • CacheItemPoolInterface : représente un groupe identifié où l’on souhaite stocker son objet Cache.

Le fonctionnement d’un cache est relativement simple : nous pouvons voir cela comme une sorte de mémo pratique qui stocke des informations pour un accès rapide ultérieur. Imaginez que ce mémo est organisé dans une boîte appelée « CachePool« . Pour récupérer des informations, nous lui donnons une clé spécifique, la boîte à mémo (CachePool) nous renvoie les informations associées à cette clé. Si elle les trouve rapidement, nous déterminons que la clé a été « touchée » ou « hit« .

Dans une implémentation basique et efficiente, l’interaction avec le CachePool se fait généralement via un DataProvider (ou fournisseur de données). Cette approche offre une séparation nette entre la logique métier de l’application et la gestion du cache, favorisant ainsi la modularité et la facilité de maintenance. Elle permet également de garantir une meilleure cohérence entre différentes parties de l’application qui accèdent au cache, tout en respectant les principes de la norme PSR-6.

Dans une implémentation basique et efficiente, on interagit avec le CachePool à travers un DataProvider (ou fournisseur de données).

Schématiquement, voici une vision simplifiée du processus : 

Le DataProvider va interagir avec le cachePool via une clé. Un item est touché, alors il est automatiquement rendu. Dans le cas contraire, nous chargeons la donnée et la mettons en cache avec cette même clé. Programmatiquement, nous arrivons donc à avoir un système résilient qui va toujours optimiser son retour de données en fonction du statut d’un objet.

L’implémentation d’un cache sur-mesure peut devenir intéressante dans plusieurs cas :

  • Lorsque l’on multiplie des requêtes à la base de données : par exemple, si des prix sont chargés pour un groupe de clients, le premier client de ce groupe chargera les prix pour les autres
  • Lorsque l’on multiplie des requêtes à un third-party : en faisant appel à un service de gestion de permissions et d’autorisation par exemple 

Nous n’avons pas besoin d’avoir en permanence la réponse puisque les permissions ne sont mises à jour qu’à un intervalle de temps mensuel. Donc le cache peut couvrir une durée de vie d’environ 1 mois et économise des requêtes vers un third-party

Utilisation du cache dans Symfony

Retour à Symfony qui est un framework PHP moderne, populaire et qui simplifie le développement d’applications web robustes et évolutives. Parmi ses nombreux composants, le Cache occupe une place essentielle. C’est un composant dit “standalone”, qui peut donc s’utiliser sur n’importe quelle solution PHP. 

Standalone (Anglicisme informatique) : Produit utilisable seul, c’est-à-dire sans modules ou connaissances complémentaires.

Le composant Cache de Symfony fournit une interface unifiée pour travailler avec différentes solutions de mise en cache et offre une abstraction puissante permettant aux développeurs de changer facilement de système de mise en cache sans modifier le code de l’application. Il prend en charge divers systèmes de stockage en cache tels que les fichiers, les bases de données, Redis, Memcached, et bien d’autres.

Configuration de base

Pour utiliser le composant Cache, vous devez tout d’abord configurer le système de mise en cache que vous souhaitez utiliser. La configuration se fait généralement dans le fichier services.yaml ou équivalent de votre application Symfony.

Voici un exemple de configuration pour utiliser le cache avec le système de fichiers :

# config/services.yaml

services:
    cache.app:
        class: Symfony\Component\Cache\Adapter\FilesystemAdapter
        arguments:
            - 'app.cache'

Dans cet exemple, `cache.app` est un service qui utilise le système de fichiers comme back-end pour le cache. Vous pouvez facilement ajuster cette configuration pour utiliser d’autres systèmes de mise en cache.

Utilisation du composant Cache

Une fois que le cache est configuré, vous pouvez l’utiliser dans votre code Symfony. Voici un exemple simple d’utilisation du cache pour stocker et récupérer des données :

// Exemple de contrôleur Symfony

use Symfony\Component\Cache\Adapter\AdapterInterface;

class SomeController extends AbstractController
{
    public function someAction(AdapterInterface $cache)
    {
        $item = $cache->getItem('my_cache_key');

        if (!$item->isHit()) {
            // Données non présentes dans le cache, les récupérer et les mettre en cache
            $data = $this->getDataFromDatabase();
            $item->set($data);
            $cache->save($item);
        } else {
            // Données présentes dans le cache, les récupérer directement
            $data = $item->get();
        }

        // Utiliser les données récupérées
        // ...

        return new Response('...');
    }
}

Explication

  • CachePool (boîte à mémo) : Dans notre exemple, $cache est notre CachePool. Il agit comme la boîte à mémo qui stocke et gère nos notes (CacheItem).
  • CacheItem (note en mémoire) : Un CacheItem est une représentation d’un objet que nous souhaitons mettre en cache. Dans notre exemple, nous utilisons la clé ‘my_cache_key’ pour identifier cette note.
  • isHit() (vérification) : Avant d’agir sur la note, nous vérifions si elle a été touchée (isHit). Si elle n’a pas été touchée, cela signifie que les données ne sont pas encore en cache, alors nous les récupérons, les enregistrons dans la note, et sauvegardons cette note dans la boîte à mémo.
  • getDataFromDatabase() (récupération des données) : C’est ici que nous simulons la récupération des données, par exemple, depuis une base de données.
  • Utilisation des données récupérées : Enfin, nous utilisons les données récupérées, que ce soit pour les afficher ou les traiter de quelque manière que ce soit.

Ce code illustre bien la manière dont Symfony, à travers son composant Cache, respecte la norme PSR-6 en organisant le stockage et la récupération de données de manière cohérente et efficace. La séparation nette entre la logique métier de l’application et la gestion du cache assure une modularité accrue, permettant ainsi une maintenance aisée et une application flexible.

Pour aller plus loin dans le Caching et sur le framework Symfony, contactez un expert Dn’D ! 
👇

Vous avez aimé ?

0