Les requêtes N+1 - a podcast by Sylvain Abélard

from 2016-02-07T15:45

:: ::

Bonjour, bienvenue pour l’épisode 3, plus proche de mon quotidien de Rubyiste,
sur un oubli classique dans Railsles requêtes N+1, un des premiers
symptômes à vérifier quand votre application est trop lente.

Je n’aime lâcher personne dans un domaine complètement inconnu, on va
commencer par une introduction superficielle à Rails et ActiveRecord,
merci comme toujours de me pardonner quelques inexactitudes qui ne sontlà que pour éviter la confusion.

Ruby on Rails / MVC

Le framework Ruby on Rails a été conçu pour faire des applications Web avec
une architecture MVC, Modèle-Vue-Contrôleur. On ne rentrera pas ici dans lesdétails mais vous pourrez trouver de nombreuses ressources sur Internet et
dans les liens de cet épisode :jeveuxapprendreruby.fret mon“Web Primer”,
une vue d’ensemble et liste de vocabulaire sur un grand nombre de conceptsutilisés dans l’architecture Web et la conception MVC.

ActiveRecord et ORM

Là encore, le but n’est pas de tout détailler. ActiveRecord est à la fois
le nom d’un concept architectural et le nom de son implémentation en Rubypour Rails.

Le principe est que l’on travaille dans un langage objet, Ruby, et que
l’on souhaite travailler avec des données venant d’une base de donnéesrelationnelles.

Rails a identifié des besoins courants, des choses que les développeurs
résolvent toujours de la même manière, et souhaite donc vous les fournirdirectement sans une ligne de code : c’est sa philosophie
“Convention over Configuration”.

Cette couche logicielle s’appelle ORM “Object-Relational Mapping”, c’est
à dire correspondance Objet - Relationnel. Elle a des avantages bien sûr,mais aussi évidemment des défauts, dont celui de cacher les difficultés.

Quel piège ?

Il est extrêmement simple en Ruby on Rails de créer des pages de référentiel,
et par exemple une liste d’Auteurs et une liste de Livres. Quand on affichela liste des Livres, on souhaite probablement afficher dans une colonne le
nom de l’auteur, et que ce soit un lien vers la fiche détaillée de l’auteur.

Votre modèle fait quelques lignes (déclaration des champs et associations),
votre vue est un HTML avec des balises pour exécuter du code Rails, qui sebase sur la présence d’une variable @livres correctement remplie.

Ne reste plus que votre contrôleur qui doit savoir comment faire le pont entre
les deux. Il faut aller chercher tous les livres en base de données.Le code Rails est très simple :

@livres = Livre.all

Dans une application réelle bien sûr, on a davantage de contraintes comme
de faire de la pagination : si vous avez plus d’une centaine de livres,il est inutile et lourd de tout mettre sur une page et on proposera de les
voir par pages de 20 à 100 livres à la fois.

Cependant, on s’expose au problème N+1 : à chaque ligne de votre Vue,
on a un objet livre duquel on voudrait connaître l’auteur. Rails vaalors faire la requête. Au lieu d’avoir fait 1 requête, vous en faites N.

Si l’on savait que l’on aurait besoin des Auteurs, on peut le demander
directement à la base de données via une jointure, qui se code ainsi :

@livres = Livre.joins(:auteur)

Et la métaphore ?

Si ce sujet semble obscur, voici ce que ferait votre première version
de code : moi, le contrôleur, dois appeler au téléphone une bibliothécaire,le modèle, qui s’occupera de parler avec son archiviste, la base de donnés,
qui est gardien de toutes les données dans sa bibliothèque.

Chaque connexion à la base de donnée est une coup de fil différent.

“Bonjour, je souhaiterais la liste de tous les livres.”

L’archiviste compile sa liste, présente à la bibliothécaire un paquet
de fiches, et comme je ne suis pas très doué avec leur jargon relationnel,le modèle me convertit tout cela en un tableau d’objets Ruby : on peut
se dire que c’est un classeur de fiches qui sont simples à lire pour moi.

Avec la satisfaction du devoir accompli, je vous donne la liasse de
feuilles correspondant à mes livres. Vous représentez la vue et vousdevez présenter le tout pour l’afficher à votre client.

À chaque fiche de livre, vous voyez le numéro de la fiche auteur, mais rien de plus.
Ruby on Rails n’a pas été chercher les auteurs par défaut, sinon quelle seraitla limite ? Aller chercher aussi leurs éditeurs, les commentaires de tous les
emprunteurs sur chacun des livres, l’historique de la bibliothèque ?Rails n’a pas été chercher tout cela, ce serait faire trop de travail dans votre dos
et vous ne comprendriez pas qu’il soit si lent pour quelques titres de livres.

Il se trouve que vous avez directement accès au numéro de la bibliothécaire,
et elle va rappeler l’archiviste, car vous lui demandez :

“Bonjour, je souhaiterais avoir le nom de l’auteur numéro 37.”

Brève recherche… Il s’agit de Molière.

Puis la seconde d’après, on remet la machine en branle pour demander :

“Bonjour, je souhaiterais avoir le nom de l’auteur numéro 81.”

Cette fois c’est Edmond Rostand.

Et ainsi de suite… pas très efficace.

Que fait alors cette jointure ?

La jointure est le simple moyen de demander à l’archiviste :

“Bonjour, je souhaiterais avoir la liste des livres et de leurs auteurs respectifs.”

Il travaillera un tout petit peu plus, mais c’est son métier et il est
très efficace. De plus, on évite le surcoût dû aux nombreux appels :composer, attendre au bout du fil que l’archiviste réponde à la bibliothécaire…

Sans compter les politesses “bonjour, merci, au revoir” qui ne sont
d’ailleurs pas très différentes de la manière dont se déroule leprotocole de communication entre votre code et votre base de données.

Quand l’archiviste a fini son travail, il me donnera une liasse
un peu plus épaisse, avec les fiches des auteurs incluses.Lorsque je vous donne la liasse, vous avez absolument tout ce qu’il vous
faut pour travailler, vous ne rappelez même pas la bibliothécaire,et tout le monde est content de travailler efficacement en respectant
l’architecture prévue, à savoir que moi seul le contrôleur devrait pouvoirappeler le modèle.

Et voilà !

La leçon est de faire comme dans la vraie vie :
restez clair et courtois, n’oubliez pas les jointures,et n’embêtez personne avec des dizaines de coups de fil rapprochés :)

Un peu de méta :

Bien sûr j’accepte les critiques sur cet épisode et les idées pour les
suivants sur twitter : @zen_m4

Après un épisode 1 technique, un épisode 2 conceptuel, un épisode 3 sur Ruby et Rails,
je vaus peut-être faire un épisode 4 philosophique, et tenter d’alterner sur ces quatrecatégories.

Merci et à bientôt !

Further episodes of Zen M-4 : Zen Metaphor

Further podcasts by Sylvain Abélard

Website of Sylvain Abélard