Nous avons mis en place un cache Varnish devant lâapplication pour servir de reverse proxy. Toutes les requĂȘtes qui sont dĂ©jĂ en cache dans Varnish sont directement servies par celui-ci. Cette opĂ©ration est un simple lookup effectuĂ© par le systĂšme Varnish. Ce dernier est hautement optimisĂ© pour la performance, ce qui rend le processus extrĂȘmement efficace. Ces requĂȘtes prennent seulement quelques millisecondes Ă ĂȘtre servies.
MĂ©triques
DâaprĂšs le monitoring de Varnish, la frĂ©quence des requĂȘtes sur 24 heures ressemble Ă ceci:

Ce screenshot a Ă©tĂ© pris lors dâune journĂ©e typique, sans Ă©vĂ©nement particulier. En journĂ©e, Varnish sert entre 40'000 et 100'000 requĂȘtes par minute, avec des pics encore plus Ă©levĂ©s. La nuit, lâactivitĂ© diminue fortement.
Environ â des requĂȘtes sont des cache hits dans Varnish. Le taux de hit est particuliĂšrement Ă©levĂ© lorsque la charge est forte, ce qui allĂšge considĂ©rablement la charge sur lâapplication backend.

Cette activité entraßne également une consommation de bande passante importante:

Optimiser au maximum la mise en cache
Toutes les requĂȘtes Ă M-API doivent ĂȘtre authentifiĂ©es. Pour atteindre un taux de cache hit Ă©levĂ©, nous utilisons le pattern "user context", qui permet de cacher les requĂȘtes par groupes dâutilisateurs ayant les mĂȘmes permissions, plutĂŽt quâau niveau de chaque utilisateur·rice authentifié·e. Tu peux en lire plus sur ce pattern dans ce blogpost.
Nous avons aussi configurĂ© Varnish pour nettoyer les paramĂštres de requĂȘte et lâen-tĂȘte Accept-Language, afin dâaugmenter encore plus le taux de cache hit.
Comme lâapplication sait quand les donnĂ©es sont modifiĂ©es, nous avons dĂ©fini une longue durĂ©e de vie du cache. Nous permettons aussi Ă lâapplication dâinvalider activement le cache si nĂ©cessaire. Nous utilisons le mĂ©canisme xkey de Varnish, qui permet de taguer toutes les rĂ©ponses et de les invalider par tag.
Beaucoup de rĂ©sultats de recherche sont des listes de produits, comme celles obtenues via une recherche en texte libre ou par navigation dans les catĂ©gories. Lorsque les donnĂ©es changent, lâordre des rĂ©sultats de recherche peut aussi changer. Comme nous utilisons un systĂšme de pagination, xkey seul ne suffit pas. Une modification affecte non seulement la page oĂč le produit apparaissait auparavant, mais aussi toutes les pages suivantes. Invalider toutes les listes Ă chaque modification de produit rendrait le cache inutile.
Pour rĂ©soudre ce problĂšme, nous utilisons Edge Side Includes (ESI). Les rĂ©ponses des listes contiennent uniquement des instructions ESI qui font rĂ©fĂ©rence aux produits Ă inclure, mais pas les donnĂ©es elles-mĂȘmes. GrĂące Ă cela, la gĂ©nĂ©ration des listes est trĂšs efficace, nous permettant dâutiliser une durĂ©e de cache courte pour elles.
Varnish interprĂšte lâESI pour rĂ©cupĂ©rer chaque produit individuellement et assembler la rĂ©ponse pour le client. Les sous-requĂȘtes pour les produits sont mises en cache plus longtemps et taguĂ©es avec leur ID produit, ce qui permet leur invalidation ciblĂ©e. Cela garantit que mĂȘme si la liste est en cache, les produits affichĂ©s sont immĂ©diatement mis Ă jour.
Nous avons Ă©galement configurĂ© le mode "grace", qui permet Ă Varnish de servir du contenu obsolĂšte si lâapplication backend ne rĂ©pond pas. Dans la plupart des cas dâusage de M-API, une information lĂ©gĂšrement dĂ©passĂ©e est prĂ©fĂ©rable Ă une absence de rĂ©ponse.
Application serveur
Tout ne peut pas ĂȘtre gĂ©rĂ© par la mise en cache HTTP. Pour les requĂȘtes qui ne peuvent pas ĂȘtre mises en cache ou dont la rĂ©ponse nâest pas encore en cache, le temps de rĂ©ponse du backend est critique. PHP, et en particulier le framework Symfony que nous utilisons, est optimisĂ© pour des temps de rĂ©ponse rapides.
PHP est conçu pour les applications stateless. Dans M-API, chaque requĂȘte est totalement indĂ©pendante, ce qui permet lâĂ©talonnage horizontal facile. Lâinfrastructure cloud surveille automatiquement la charge de lâapplication et ajoute ou supprime des instances en fonction de la demande.
Pour optimiser les temps de rĂ©ponse, nous avons utilisĂ© un profiler pour identifier les goulots dâĂ©tranglement et nous assurer que chaque amĂ©lioration apportait un rĂ©el gain. Nous avons identifiĂ© des donnĂ©es que nous avons ensuite stockĂ©es dans un cache applicatif (Redis), ce qui nous a permis de rĂ©duire considĂ©rablement les requĂȘtes vers MySQL.
AprÚs cette amélioration, une part significative du temps de traitement était consacrée à la conversion des objets en JSON. Nous avons exploré plusieurs solutions pour accélérer ce processus, avec au final une solution beaucoup plus rapide. Tu peux en lire plus dans cet article de blog.
GrĂące Ă ces optimisations, nous avons atteint des temps de rĂ©ponse dâenviron 50 millisecondes pour les rĂ©ponses produits â mĂȘme lorsque chaque produit contient plusieurs centaines de KB Ă plus dâun MB de donnĂ©es JSON.