Transmettre les mises à jour des tâches Sidekiq via ActionCable

Posted by ZedTuX 0n R00t on June 27, 2019

Dans cet article je vous explique comment j’ai implémenté la mise à jour de l’affichage des tâches Sidekiq en temps réel dans mon application web.

Je suis actuellement en train de développer Commis qui a une zone d’affichage des tâches en cours d’exécution, comme par exemple l’import d’un dépôt Chef, ou le provisionnement d’une machine etc …

Screenshot-2019-06-27-at-22.35.06

Le besoin

Il me faut un moyen simple et automatisé pour transmettre les mises à jours des tâches sidekiq via ActionCable.

Les middleware de Sidekiq

Le projet Sidekiq permet de déjà capturer les messages ajoutés, qui tourne et qui finissent avec succès grâce aux middlewares Sidekiq:

  • Le middleware coté client qui permet de faire quelque chose avant qu’une tâche ne soit ajoutée.
  • Le middleware coté serveur qui permet de faire quelque chose avant qu’une tâche ne démarre, puis une fois que la tâche s’est terminée correctement.

Les tâches qui se terminent en erreur

Dans la méthode call du middleware serveur, il y a un yield qui va exécuter votre tâche.

Mais si celle-ci jette une erreur, vous n’en serrez pas informé, et vous ne recevrez donc pas l’information que la tâche s’est arrêté en erreur.

J’ai géré ce cas avec un rescue StandardError => error comme vous pouvez le voir dans mon middleware server.

La classe StandardError est celle dont toutes les exceptions gérée (à l’inverse de la classe Exception qui peut être jettée si vous avez une erreur de syntaxe) donc toutes erreurs seront attrapées et passerons dans le block de code où je transmets l’information par ActionCable.

Donc jusqu’ici nous avons les informations lorsqu’une tâche :

  • est ajoutée à Sidekiq
  • démarre dans Sidekiq
  • se termine avec succès ou sur erreur

Il nous manque un dernier cas …

Les tâches supprimées de Sidekiq

Disons que dans l’onget Retries de Sidekiq UI vous avez une ou des tâches, et que vous en supprimez une. Pas de mises à jour dans votre application.

Les middleware de Sidekiq ne gèrent pas cette action, or dans mon cas cela est nécessaire afin de garder une cohérence entre l’affichage dans Commis, et Sidekiq.

Pour se faire, j’ai été obligé de surcharger Sidekiq::JobSet.delete_by_value.

Plus exactement, j’ai fait un alias de la méthode que j’appelle depuis une nouvelle méthode qui va transmettre l’information de la tâche supprimée comme vous pouvez le voir dans l’initialiser Sidekiq de Commis, mais que je vous le remet ici :

Pour la petite explication :

Ligne 1: Tout d’abord un require de sidekiq/api afin de charger la classe pour pouvoir la ré-ouvrir, ou la surcharger.

Ligne 3 et 4: Puis je redéclare à l’identique la signature de la classe.

Ligne 5: Je créé un alias old_delete_by_value de la méthode delete_by_value. Ceci me permet plus tard d’appeler l’ancienne méthode à la ligne 11 afin de garder le comportement normal de cette méthode.

Ligne 6: Je créé une nouvelle méthode avec le même nom que la méthode dont j’ai fait l’alias juste avant, afin que Sidekiq l’appelle.

Ligne 7: Je lis le JSON de la variable value afin de pouvoir l’utiliser plus bas (Sidekiq passe les attributs des tâches au format JSON mais échappés dans une chaine de caractère. Pour pouvoir manipuler le JSON, il faut l’interprêter avec JSON.parse)

Ligne 9: Je transmets le message via ActionCable. (La classe CommisChannel::BackgroundTasks est responsable du formattage du message envoyé à l’interface comme il le faut, puis de le passer dans la méthode broadcast de ActionCable. Vous pouvez voir son code source ici).

Ligne 11: Je fini par exécuter l’ancienne méthode qui va elle réellement effacer la tâche de Redis.