Ruby On Rails – Des tâches récurrentes simples avec Whenever

Il arrive que dans la vie d’un projet on ait besoin d’exécuter certaines tâches de manières récurrentes (export/import de données, suppression de vieux comptes, envoi de mail, …).

Sous Linux, la méthode la plus simple est d’utiliser l’utilitaire cron : il nous permet de lancer commandes, et scripts à une date et une heure précise et/ou de manière récurrente.

Exemples.

# s'exécute tous les jours à 23h30
30 23 * * * df >> /tmp/df.log

# s'exécute toutes les 5 minutes
*/5 * * * * my-script.sh

Donc si on veut mettre en place les tâches récurrentes de notre projet Rails, il nous suffit de :

  1. nous connecter sur le serveur
  2. modifier la crontab pour y inclure nos nouvelles tâches

cron est un utilitaire réputé, il existe donc de très nombreuses ressources pour apprendre à l’utiliser. Mais il y a plusieurs choses qui me gênent :

  • la syntaxe cron pour définir les heures et la récurrence est parfois dure à relire (ex. 0 0 25-31 1,3,5,7,8,10,12 0 my-script.sh )
  • la modification de la crontab peut avoir un impact global pas forcément prévu
  • on ne peut pas connaître l’ensemble des tâches récurrentes d’un projet
  • le fait de devoir aller sur le serveur pour mettre à jour la crontab, oblige à le faire sur chaque machine sur laquelle l’application est déployée, il peut donc y avoir des oublis

C’est pourquoi j’ai pris l’habitude d’utiliser la gem Whenever pour gérer les tâches récurrentes de mes projets Rails

Cette gem une fois ajouté à votre projet vous permettra de définir dans un fichier Ruby, les tâches récurrentes de votre projet, et ensuite de les ajouter aux tâches cron existantes.

Installation

cd my_project_rails

# ajoute la gem whenever à votre projet
bundle add whenever

# génère un fichier config/schedule.rb
wheneverize .

Utilisation

Pour utiliser Whenever c’est très simple éditez, le fichier config/schedule.rb . La déclaration des tâches et leur programmation se fait de manière simple et lisible.

every 1.day do
  rake 'my_task:rake'
end

every 2.week, at: '2:00 am' do
  command '/path/to/backup_bdd.sh'
end

every :month do
  runner 'User.export_last_month_new_users'
end

Par défaut, la gem vous permet d’exécuter 3 types de job : une commande shell, une tâche Rake, ou une méthode de votre projet via la commande rails runner. Mais néanmoins, il est possible, si vous avez besoin, de définir des types de jobs custom.

Une fois vos tâches définies, vous pouvez exécuter la commande whenever pour obtenir les tâches cron telles que vous auriez du les écrire vous-même. Si cela vous convient, vous pouvez alors déployer votre code sur un serveur.

Connectez-vous ensuite sur ce dernier, et lancez la commande whenever –update-crontab pour mettre à jour la crontab. Cette commande permet aussi de gérer l’ajout, la modification, ou la suppression de tâches dans le fichier config/schedule.rb  et ainsi garder une crontab à jour.

Mise à jour de la crontab automatique

On a donc maintenant, grâce à Whenever une syntaxe plus simple pour programmer une tâche, et une mise à jour plus safe de la crontab; mais on a encore besoin de se connecter sur notre serveur après chaque déploiement pour maintenir la crontab à jour.

Heureusement pour nous, Whenever vient avec une intégration Capistrano pour résoudre ce dernier problème.

Voici comment procéder

# dans le fichier Capfile ajoutez la ligne suivante
require "whenever/capistrano"

# dans le fichier config/deploy.rb ajouter la ligne suivante
# pour permettre à whenever de mieux identifier quelle crontab
# il doit mettre à jour
set :whenever_identifier, -> { "#{fetch(:application)}_#{fetch(:stage)}" }

Si vous utilisez RBENV

Whenever ne supporte pas par défaut les gestionnaires de version Ruby. Ce qui signifie que lorsque cron essayera de lancer une de vos tâches il y a des risques d’avoir des erreurs comme quoi cron n’a pas pu trouver Ruby ou Bundler.

Si vous utilisez RBENV, il va falloir redéfinir les types par défaut pour qu’ils utilisent Ruby et les gems de l’environnement courant.

# ajoutez ceci au début du fichier config/schedule.rb

if defined? :rbenv_root
  job_type :rake,    %{cd :path && :environment_variable=:environment :rbenv_root/bin/rbenv exec bundle exec rake :task --silent :output}
  job_type :runner,  %{cd :path && :rbenv_root/bin/rbenv exec bundle exec rails runner -e :environment ':task' :output}
  job_type :script,  %{cd :path && :environment_variable=:environment :rbenv_root/bin/rbenv exec bundle exec script/:task :output}
end

 

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée.

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.