Automatizar tareas periódicas en Rails/Linux (no capistrano)


NOTA: Tutorial para servidores de/en producción

Ahorita estoy trabajando con algo bastante tedioso: Listas de correo. El problema con ellas es que sin importar cuánto las valides y limpies, siempre hay un correo que no se envía por error de formato en la dirección de Email.

El caso es, que cuando hay un error de estos y estás enviando un newsletter de forma automática a muchas personas, el envío se detiene y hay que hacer circo maroma y teatro para reanudarlo (encontrar el newslettter con correo érroneo, el índice de dicho correo, limpiarlo de la base de datos de correo, descartar todas las direcciones a las que ya se les envió además de esa y volver a empezar son solo algunas de las tareas a realizar), y hay que estar 100% al pendiente de cuando un error suceda para hacer toda la gestión de reinicio.

Afortunadamente, hay una manera de automatizar este proceso sin necesidad de que una persona esté checando el servidor a cada rato y aplique todo el proceso explicado arriba: Cron. El problema con Cron es que su sintaxis es HORRIBLE y difícil tanto de escribir como de entender (hasta cierto punto) Por suerte, en Rails tenemos una gema que se llama "whenever", la cual nos permitirá automatizar este tipo de tareas en el servidor.

Para usarla simplemente agregamos a nuestro Gemfile lo siguiente:

 gem 'whenever', require: false

Hacemos bundle install y luego corremos:

bundle exec wheneverize

eso nos creará un archivo llamado "schedule.rb" en nuestra carpeta "config" de la aplicación, en el cuál podremos añadir todos los cronjobs que requiramos para la aplicación con una dulce sintaxis ruby (ver el código comentado en dicho archivo para ejemplos o leer la documentación de la gema por acá).

Un ejemplo del mío:


La primera línea define dónde deberá guardarse el output resultante de correr los cronjobs y las demás son cronjobs programados. El primero corre una rake task cada día en un tiempo específico del mismo y el segundo cada 2 minutos corre un método llamado "scruffy" que se encuentra definido en mi application.rb y se encarga de hacer en automático toda la gestión de "reenvío en caso de falla" de la que les hablé arriba.

Ya definido este archivo con lo que queremos, tenemos que correr los siguientes comandos:

1. bundle exec whenever
2. whenever -i
3. crontab -l

El primero debería soltarnos la sintaxis cron de los cronjobs que definimos en ruby o si algo está mal un error indicándonos qué línea corregir. El segundo escribirá la crontable para dichas definiciones y el tercero nos mostrará la nueva crontable ya escrita.

Ahora tenemos que hacer ejecutable un archivo que encontraremos en la carpeta "script" de nuestra aplicación Rails y se llama "rails" precisamente:



Finalmente, reiniciamos cron y nuestro servidor (yo uso Nginx) con los siguientes comandos:

1. sudo service cron restart
2. sudo service nginx restart

y habremos terminado, revisamos nuestra aplicación para asegurarnos de que el comportamiento automatizado deseado se esté ejecutando cuando lo pedimos y eso es todo, automatización al instante en Rails.