Mise à niveau de DelayedJob vers Sidekiq dans une webapp Rails mature

Leonardo Brito

Changer un composant essentiel à une mission à partir d'une grande application Web mature peut sembler une tâche intimidante. Essayer de le faire tout en gardant l'application en cours d'exécution et en servant des milliers de clients semble ajouter une insulte aux blessures – mais est tout à fait faisable si vous jouez correctement vos cartes (alerte spoiler: faites-le progressivement). C'est l'histoire de la façon dont nous avons remplacé un logiciel essentiel dans une grande base de code Rails avec aucun temps d'arrêt et aucun manque de données.

Au début de 2017, peu de temps après la mise à niveau de l'un des projets de notre client de Rails 4 à 5, nous recherchions la prochaine grande amélioration à apporter. Nous avons fait une introspection et avons trouvé un fruit juteux à suspendre dans notre jardin de code: remplacer DelayedJob, un joyau Ruby utilisé pour mettre en file d'attente et exécuter de manière asynchrone des tâches ou des "travaux" à forte intensité de temps, avec quelque chose de plus rapide et moins gourmand en disque .

DelayedJob fonctionne très bien, mais il présente quelques lacunes importantes par rapport aux alternatives plus récentes:

  1. Il stocke les travaux dans la base de données, ce qui signifie que le trafic d'E / S lié au travail est en concurrence avec tout le monde – et, finalement, avec l'utilisateur final. Les travaux peuvent généralement attendre, tandis que les clients ne devraient définitivement pas avoir à le faire.
  2. Chaque travail s'exécute sur son propre processus Ruby individuel, plaçant une surcharge non négligeable sur la concurrence, ce qui se traduit par un débit de travail plus petit et une évolutivité réduite.

Heureusement, il existe plusieurs alternatives à DelayedJob, dont beaucoup s'attaquent aux problèmes ci-dessus en utilisant une base de données en mémoire (résout le problème 1) et une approche à processus unique à plusieurs threads (problème 2). Nous avons choisi d'utiliser Sidekiq, un joyau largement populaire qui utilise Redis comme magasin de données en mémoire et s'exécute sur un seul processus, générant un thread par tâche.

Planifier la transition

Bien que Sidekiq soit de quelques ordres de grandeur plus rapide que DelayedJob, ici à Guava nous ne nourrissons pas l'illusion que meilleure performance équivaut toujours à meilleure solution. Nous savons que changer de gros morceaux d'une application qui est en production depuis longtemps peut être très délicat et ne doit pas être pris à la légère; et les dizaines de composantes d'emplois de ce projet, avec une moyenne de plus de 60 000 tâches traitées par jour, sont définitivement classées comme un gros morceau. Cela signifie qu'avant toute autre chose, et certainement avant tout codage réel, nous devons nous poser les questions suivantes:

  • Que font exactement les emplois actuellement? Affectent-ils les parties critiques du système telles que les paiements ou les paiements?
  • Est-il techniquement possible de faire le changement? Existe-t-il des couplages de code désagréables, des dépendances ou tout autre élément qui pourrait rendre la mise à niveau impossible ou tout simplement pas la peine?
  • Pouvons-nous déployer une transition progressive et en douceur, en introduisant progressivement la nouvelle bibliothèque tout en maintenant l'ancienne, ou avons-nous besoin d'un commutateur "hard fork", coupant une bibliothèque entièrement avant de démarrer la nouvelle?
  • Comment pouvons-nous nous protéger contre la corruption ou la perte de données en cas de problème?

Toutes ces questions devaient être répondues afin que nous puissions évaluer la viabilité de la mise à niveau. Après avoir examiné notre base de code et étudié Sidekiq, nous avons conclu:

  • Les tâches asynchrones étaient utilisées à la fois dans des tâches critiques et non critiques, allant du paiement par le client à l'envoi d'e-mails promotionnels.
  • Nous pourrions utiliser les extensions DelayedExtensions de Sidekiq (spécialement conçues pour aider les gens à passer de DelayedJob à Sidekiq) pour rationaliser la mise à niveau.
  • Nous commencerions la transition en exécutant un seul travail d'envoi d'e-mails non critique avec Sidekiq et nous partirions de là.
  • Nous nous assurerions que Sidekiq ne manquait aucune donnée avant d'essayer d'augmenter la quantité de tâches qu'il gère.

En d'autres termes, nous ferions les choses comme nous aimons ici à Guava: progressivement.

Après avoir mis en place la nouvelle infrastructure nécessaire à Sidekiq, nous avons commencé la migration en remplaçant une seule tâche non critique qui était auparavant gérée par DelayedJob: envoyer un e-mail aux administrateurs du site. En déployant d'abord cette tâche non critique unique, nous pourrions facilement surveiller le comportement de Sidekiq et résoudre rapidement tout problème. Nous «trempions nos orteils dans l'eau», pour ainsi dire – si quelque chose tournait mal avec la nouvelle infrastructure, rien de vraiment grave ne pouvait arriver aux opérations du client.

Une fois satisfaits et confiants de la nouvelle infrastructure, nous avons divisé les types d'emplois restants en une demi-douzaine d'ensembles regroupés par importance et type d'emploi. Nous avons déployé un ensemble à la fois – chaque ensemble étant une demande de tirage distincte, afin que nous puissions revenir sans douleur si quelque chose allait au sud – puis nous avons observé les travaux démarrant à Sidekiq et les files d'attente DelayedJob correspondantes s'éteignant. Nous avons attendu que la file d'attente DelayedJob soit vide pendant quelques jours pour nous assurer qu'aucun travail en suspens n'était encore généré dans un coin éloigné de la base de code. Une fois que nous avons été sûrs que la transition était terminée, nous avons commencé – également progressivement – à fermer l'ancienne infrastructure, en supprimant d'abord les tables DelayedJob et enfin en supprimant la gemme elle-même. Phew!

Profiter des avantages

Nous avons déjà mentionné quelques avantages de Sidekiq par rapport à DelayedJob en termes de performances. Nous pourrions également mentionner, en ajoutant à l'ensemble des avantages tirés de cette mise à niveau, que Sidekiq a une interface utilisateur plus agréable, nous permettant de vérifier rapidement si tout fonctionne bien et même si l'entreprise est en pleine effervescence ou en déclin. Notre base de code s'est également améliorée, remplaçant une interface DelayedJob lourde en métaprogrammation par une configuration Sidekiq basée sur un travailleur plus sain. L'implémentation actuelle est sans doute beaucoup plus à l'épreuve du temps également, car DelayedJob a tendance à être moins utilisé de nos jours et pourrait devenir l'une de ces bibliothèques héritées mal prises en charge que les développeurs saluent avec des soupirs et des froncements de sourcils.

En un mot, la mise à niveau DelayedJob-to-Sidekiq signifiait rembourser une partie de la dette technique du projet avec une grosse vérification minutieuse, des changements progressifs et des bonnes pratiques de programmation. La fluidité de la transition, sans interruption de service et sans perte de données, est un corollaire de la prudence que nous employons toujours ici à Guava.