La mise en cache est essentielle pour améliorer les performances et l’évolutivité des applications web – et la mise en cache dans Ruby on Rails ne fait pas exception. En stockant et en réutilisant les résultats de calculs coûteux ou de requêtes de base de données, la mise en cache réduit considérablement le temps et les ressources nécessaires pour répondre aux requêtes des utilisateurs.

Ici, nous examinons comment mettre en œuvre différents types de mise en cache dans Rails, tels que la mise en cache de fragments et la mise en cache de poupées russes. Nous vous montrons également comment gérer les dépendances de la mise en cache et choisir les magasins de mise en cache, et nous décrivons les meilleures pratiques pour utiliser efficacement la mise en cache dans une application Rails.

Cet article suppose que vous connaissiez Ruby on Rails, que vous utilisez la version 6 ou supérieure de Rails et que vous êtes à l’aise avec les vues Rails. Les exemples de code montrent comment utiliser la mise en cache dans des modèles de vues nouveaux ou existants.

Types de mise en cache dans Ruby on Rails

Plusieurs types de mise en cache sont disponibles dans les applications Ruby on Rails, en fonction du niveau et de la granularité du contenu à mettre en cache. Les principaux types utilisés dans les applications Rails modernes sont les suivants :

  • Mise en cache de fragments : met en cache les parties d’une page web qui ne changent pas fréquemment, telles que les en-têtes, les pieds de page, les colonnes latérales ou le contenu statique. La mise en cache des fragments réduit le nombre de partiels ou de composants rendus à chaque requête.
  • Mise en cache des poupées russes : met en cache les fragments imbriqués d’une page web qui dépendent les uns des autres, tels que les collections et les associations. La mise en cache des poupées russes évite les requêtes inutiles à la base de données et facilite la réutilisation des fragments mis en cache inchangés.

Deux autres types de mise en cache faisaient auparavant partie de Ruby on Rails, mais sont désormais disponibles en tant que fonctions distinctes :

  • Mise en cache des pages : met en cache des pages web entières sous forme de fichiers statiques sur le serveur, en contournant l’ensemble du cycle de vie du rendu de la page
  • Mise en cache des actions : met en cache la sortie d’actions de contrôleurs entiers. Il est similaire à la mise en cache de pages mais vous permet d’appliquer des filtres comme l’authentification.

La mise en cache des pages et des actions est rarement utilisée et n’est plus recommandée pour la plupart des cas d’utilisation dans les applications Rails modernes.

Mise en cache de fragments dans Ruby on Rails

La mise en cache de fragments vous permet de mettre en cache des parties d’une page qui changent peu souvent. Par exemple, une page affichant une liste de produits avec leurs prix et leurs évaluations peut mettre en cache des détails qui ne changeront probablement pas.

Dans le même temps, cela pourrait permettre à Rails de rendre à nouveau les parties dynamiques de la page – comme les commentaires ou les évaluations – à chaque chargement de la page. La mise en cache de fragments est moins utile lorsque les données sous-jacentes d’une vue changent fréquemment en raison de la surcharge liée à la mise à jour fréquente du cache.

En tant que type de mise en cache le plus simple inclus dans Rails, la mise en cache de fragments devrait être votre premier choix lorsque vous ajoutez une mise en cache à votre application pour en améliorer les performances.

Pour utiliser la mise en cache de fragments dans Rails, utilisez la méthode d’aide cache dans vos vues. Par exemple, écrivez le code suivant pour mettre en cache un produit partiel dans votre vue :

<% @products.each do |product| %>
  <% cache product do %>
    <%= render partial: "product", locals: { product: product } %>
  <% end %>
<% end %>

L’aide cache génère une clé de cache basée sur le nom de la classe de chaque élément, id, et l’horodatage updated_at (par exemple, products/1-20230501000000). La prochaine fois qu’un utilisateur demandera le même produit, l’aide cache ira chercher le fragment mis en cache dans le magasin de cache et l’affichera sans lire le produit dans la base de données.

Vous pouvez également personnaliser la clé de cache en passant des options à l’aide cache. Par exemple, pour inclure un numéro de version ou un horodatage dans votre clé de cache, écrivez quelque chose comme ceci :

<% @products.each do |product| %>
  <% cache [product, "v1"] do %>
    <%= render partial: "product", locals: { product: product } %>
  <% end %>
<% end %>

Vous pouvez également définir un délai d’expiration :

<% @products.each do |product| %>
  <% cache product, expires_in: 1.hour do %>
    <%= render partial: "product", locals: { product: product } %>
  <% end %>
<% end %>

Le premier exemple ajoutera v1 à la clé du cache (par exemple, products/1-v1). Cela permet d’invalider le cache lorsque vous modifiez le modèle partiel ou la mise en page. Le deuxième exemple définit un délai d’expiration pour l’entrée du cache (1 heure), ce qui permet d’expirer les données périmées.

La mise en cache en poupées russes dans Ruby on Rails

La mise en cache en poupées russes est une puissante stratégie de mise en cache dans Ruby on Rails qui optimise les performances de votre application en imbriquant les caches les uns dans les autres. Elle utilise la mise en cache de fragments Rails et les dépendances de cache pour minimiser le travail redondant et améliorer les temps de chargement.

Dans une application Rails typique, vous rendez souvent une collection d’éléments, chacun avec plusieurs composants enfants. Lorsque vous mettez à jour un seul élément, évitez de rendre à nouveau l’ensemble de la collection ou les éléments non affectés. Utilisez la mise en cache de la poupée russe lorsque vous traitez des structures de données hiérarchiques ou imbriquées, en particulier lorsque les composants imbriqués ont leurs propres données associées qui peuvent changer indépendamment.

L’inconvénient de la mise en cache en poupée russe est qu’elle ajoute de la complexité. Vous devez comprendre les relations entre les niveaux imbriqués des éléments que vous mettez en cache pour vous assurer que vous mettez en cache les bons éléments. Dans certains cas, vous devrez ajouter des associations à vos modèles Active Record pour que Rails puisse déduire les relations entre les éléments de données mis en cache.

Comme pour la mise en cache de fragments ordinaires, la mise en cache de poupées russes utilise la méthode d’aide cache. Par exemple, pour mettre en cache une catégorie avec ses sous-catégories et ses produits dans votre vue, écrivez quelque chose comme ceci :

<% @categories.each do |category| %>
  <% cache category do %>
    <h2><%= category.name %></h2>
    <% category.subcategories.each do |subcategory| %>
    <% cache subcategory do %>
    <h3><%= subcategory.name %></h3>
    <% subcategory.products.each do |product| %>
    <% cache product do %>
        <%= render partial: "product", locals: { product: product } %>
        <% end %>
    <% end %>
    <% end %>
    <% end %>
  <% end %>
<% end %>

L’aide cache stockera chaque niveau imbriqué séparément dans le stockage de cache. La prochaine fois que la même catégorie sera demandée, elle ira chercher son fragment mis en cache dans le magasin de cache et l’affichera sans effectuer de nouveau le rendu.

Toutefois, si les détails d’une sous-catégorie ou d’un produit changent, comme son nom ou sa description, cela invalide le fragment mis en cache, qui est alors affiché à nouveau avec les données mises à jour. La mise en cache en poupées russes vous évite d’avoir à invalider une catégorie entière si une seule sous-catégorie ou un seul produit change.

Gestion des dépendances du cache dans Ruby on Rails

Les dépendances du cache sont des relations entre les données mises en cache et leurs sources sous-jacentes, et leur gestion peut s’avérer délicate. Si les données sources changent, toutes les données mises en cache associées doivent expirer.

Rails peut utiliser les horodatages pour gérer automatiquement la plupart des dépendances du cache. Chaque modèle Active Record possède les attributs created_at et updated_at qui indiquent quand le cache a créé ou mis à jour l’enregistrement pour la dernière fois. Pour que Rails puisse gérer automatiquement la mise en cache, définissez les relations de vos modèles Active Record comme suit :

class Product < ApplicationRecord
  belongs_to :category
end
class Category < ApplicationRecord
  has_many :products
end

Dans cet exemple :

  • Si vous mettez à jour un enregistrement de produit (par exemple, en modifiant son prix), son horodatage updated_at change automatiquement.
  • Si vous utilisez cet horodatage dans votre clé de cache (comme products/1-20230504000000), votre fragment mis en cache est automatiquement invalidé.
  • Pour invalider le fragment mis en cache de votre catégorie lorsque vous mettez à jour un enregistrement de produit – peut-être parce qu’il affiche des données agrégées comme le prix moyen – utilisez la méthode touch dans votre contrôleur (@product.category.touch) ou ajoutez une option touch dans votre association de modèle (belongs_to :category touch: true).

Un autre mécanisme de gestion des dépendances du cache consiste à utiliser des méthodes de mise en cache de bas niveau – telles que fetch et write – directement dans vos modèles ou vos contrôleurs. Ces méthodes vous permettent de stocker des données ou du contenu arbitraires dans votre magasin de cache avec des clés et des options personnalisées. Par exemple :

class Product < ApplicationRecord
  def self.average_price
    Rails.cache.fetch("products/average_price", expires_in: 1.hour) do
    average(:price)
    end
  end
end

Cet exemple montre comment mettre en cache des données calculées – telles que le prix moyen de tous les produits – pendant une heure en utilisant la méthode fetch avec une clé personnalisée (products/average_price) et une option d’expiration (expires_in: 1.hour).

La méthode fetch tente d’abord de lire les données dans la mémoire cache. Si elle ne trouve pas les données ou si les données ont expiré, elle exécute le bloc et stocke le résultat dans la mémoire cache.

Pour invalider manuellement une entrée de cache avant son expiration, utilisez la méthode write avec l’option force:

Rails.cache.write("products/average_price", Product.average(:price), force: true))

Stockages de cache et backends dans Ruby on Rails

Rails vous permet de choisir différents stockages de cache ou backends pour stocker vos données et contenus mis en cache. Le stockage de cache Rails est une couche d’abstraction qui fournit une interface commune pour interagir avec différents systèmes de stockage. Un backend de cache implémente l’interface du stockage de cache pour un système de stockage spécifique.

Rails prend en charge plusieurs types de stockages de cache ou de backend, détaillés ci-dessous.

Stockage de mémoire

Le stockage de mémoire utilise un hachage en mémoire comme stockage de cache. Il est rapide et simple, mais sa capacité et sa persistance sont limitées. Cette mémoire cache convient aux environnements de développement et de test ou aux petites applications simples.

Stockage sur disque

Le stockage sur disque utilise des fichiers sur le disque comme mémoire cache. Il s’agit de l’option de mise en cache la plus lente de Rails, mais sa capacité et sa persistance sont importantes. Le stockage sur disque convient aux applications qui doivent mettre en cache de grandes quantités de données et qui n’ont pas besoin de performances maximales.

Redis

Le stockage Redis utilise une instance Redis pour le stockage du cache. Redis est un stockage de données en mémoire qui prend en charge plusieurs types de données. Bien qu’il soit rapide et flexible, il nécessite un serveur et une configuration distincts. Il convient aux applications qui doivent mettre en cache des données complexes ou dynamiques qui changent fréquemment. Redis est un choix idéal pour l’exécution d’applications Rails dans le cloud, car certains hébergeurs, dont Kinsta, proposent Redis en tant que cache d’objets persistants.

Memcached

Le stockage Memcached utilise une instance Memcached pour le stockage du cache. Memcached est un stockage clé-valeur en mémoire qui prend en charge des types de données et des fonctionnalités simples. Il est rapide et évolutif, mais comme Redis, il nécessite un serveur et une configuration distincts. Ce stockage convient aux applications qui doivent mettre en cache des données simples ou statiques faisant l’objet de mises à jour fréquentes.

Vous pouvez configurer votre stockage de cache dans vos fichiers d’environnement Rails (par exemple, config/environments/development.rb) à l’aide de l’option config.cache_store. Voici comment utiliser chacune des méthodes de mise en cache intégrées à Rails :

# Use memory store
config.cache_store = :memory_store
# Use disk store
config.cache_store = :file_store, "tmp/cache"
# Use Redis
config.cache_store = :redis_cache_store, { url: "redis://localhost:6379/0" }
# Use Memcached
config.cache_store = :mem_cache_store, "localhost"

Vous ne devez avoir qu’un seul appel à config.cache_store par fichier d’environnement. Si vous en avez plusieurs, le stockage de cache n’utilise que le dernier.

Chaque stockage de cache présente des avantages et des inconvénients uniques en fonction des besoins et des préférences de votre application. Choisissez celui qui convient le mieux à votre cas d’utilisation et à votre niveau d’expérience.

Meilleures pratiques pour la mise en cache dans Ruby on Rails

L’utilisation de la mise en cache dans votre application Rails peut considérablement améliorer ses performances et son évolutivité, en particulier lorsque vous mettez en œuvre les meilleures pratiques suivantes :

  • Mettez en cache de manière sélective : Ne mettez en cache que les données fréquemment consultées, coûteuses à générer ou rarement mises à jour. Évitez la mise en cache excessive pour prévenir l’utilisation excessive de la mémoire, les risques de données périmées et la dégradation des performances.
  • Expirer les entrées du cache : Empêchez les données périmées en expirant les entrées non valides ou non pertinentes. Utilisez des horodatages, des options d’expiration ou l’invalidation manuelle.
  • Optimisez les performances du cache : Choisissez le stockage de cache qui correspond aux besoins de votre application et réglez ses paramètres – comme la taille, la compression ou la sérialisation – pour obtenir des performances optimales.
  • Surveillez et testez l’impact du cache : Évaluez le comportement du cache – comme le taux de réussite, le taux d’échec et la latence – et évaluez leurs impacts respectifs sur les performances (temps de réponse, débit, utilisation des ressources). Utilisez des outils tels que New Relic, les journaux Rails, les notifications ActiveSupport ou le mini-profiler Rack.

Résumé

La mise en cache Ruby on Rails améliore les performances et l’évolutivité des applications en stockant et en réutilisant efficacement les données ou les contenus fréquemment consultés. Avec une compréhension plus approfondie des techniques de mise en cache, vous êtes mieux équipé pour fournir des applications Rails plus rapides à vos utilisateurs.

Pour déployer votre application Rails optimisée, vous pouvez vous tourner vers la plateforme d’hébergement d’applications de Kinsta. Commencez gratuitement avec un compte Hobby Tier et explorez la plateforme avec cet exemple de démarrage rapide de Ruby on Rails.

Steve Bonisteel Kinsta

Steve Bonisteel is a Technical Editor at Kinsta who began his writing career as a print journalist, chasing ambulances and fire trucks. He has been covering Internet-related technology since the late 1990s.