Entrada

Resque en operaciones: iniciando, deteniendo y matando workers

Resque en operaciones: iniciando, deteniendo y matando workers

Serie Resque — parte 1 de 3

  1. Estás aquí — Infra: iniciando, deteniendo, matando
  2. Diagnóstico por la consola
  3. Manipulación masiva de jobs y workers

Por qué existe esta serie — trabajé mucho tiempo en proyectos de empresas que usaban Sidekiq y, durante ese proceso, fui compilando mis notas en la serie de Sidekiq. Después terminé entrando a un proyecto que usa Resque — eso generó nuevas notas, que mantuve con la misma estructura que las que ya había hecho para Sidekiq. Los problemas operacionales son los mismos (colas que se llenan, jobs que fallan en lote, deploy que necesita drenar workers), pero las herramientas cambian bastante. Esta serie es el espejo de aquella en los temas que se traducen bien: infra, diagnóstico y manipulación masiva, con los comandos equivalentes en Resque. Cuando algún tema tiene un paralelo directo, voy a enlazar con el post correspondiente de la serie Sidekiq.

La primera diferencia importante de modelo: Resque es process-per-job, Sidekiq es thread-per-job. Cada worker de Resque escucha una o más colas y, al tomar un job, hace fork de un proceso hijo que ejecuta el job y muere. Esto cambia la forma en que se inicia, drena y mata — porque siempre hay dos procesos relacionados (el worker padre y el hijo que está ejecutando).

Documentación de la API: github.com/resque/resque.

Iniciando Resque

1
QUEUE=* bundle exec rake resque:work
  • QUEUE=* hace que el worker consuma todas las colas que existan en Redis.
  • QUEUE=critical,high,default escucha en orden de prioridad — vacía critical antes de tocar high.

Levantando varios workers en una sola máquina:

1
COUNT=5 QUEUE=* bundle exec rake resque:workers

En segundo plano con PID file (útil para kill después):

1
PIDFILE=./tmp/pids/resque.pid BACKGROUND=yes QUEUE=* bundle exec rake resque:work

En producción esto vive en systemd o foreman, pero para VPS o ambiente de prueba el BACKGROUND=yes + PIDFILE es suficiente.

A diferencia de Sidekiq, no existe -C config/sidekiq.yml. La concurrencia en Resque viene de levantar N procesos (no threads), entonces la “configuración” es cuántos rake resque:work tenés corriendo.

Drenando con gracia (QUIT — recomendado)

1
kill -QUIT $(cat tmp/pids/resque.pid)

QUIT es la señal amable: el worker espera a que el job actual termine (en el proceso hijo), luego sale limpiamente. El equivalente moral de sidekiqctl stop de la parte 1 de la serie Sidekiq — preserva idempotencia si el worker está en medio de una operación no atómica.

Si no tenés el PID file, podés extraerlo con ps:

1
2
ps -ef | grep '[r]esque' | grep -v 'master' | awk '{print $2}'
kill -QUIT $(ps -ef | grep '[r]esque' | grep -v 'master' | awk '{print $2}')

El [r]esque es el truco clásico para evitar que el propio grep aparezca en el resultado.

Matando al hijo pero manteniendo el worker (TERM/USR1)

Como Resque hace fork por job, podés matar solo el job en ejecución sin derribar el worker padre:

1
2
3
4
5
# TERM en el worker: mata al hijo en el momento Y cierra el worker
kill -TERM $(cat tmp/pids/resque.pid)

# USR1 en el worker: mata al hijo en el momento, pero el worker sigue y toma el próximo job
kill -USR1 $(cat tmp/pids/resque.pid)

USR1 es la herramienta correcta para “este job específico se trabó y está bloqueando el worker, pero no quiero tirar abajo la infraestructura”. El job se convierte en failure, el worker vuelve al loop.

Pausando sin matar (USR2 / CONT)

1
2
3
4
5
# USR2: deja de tomar jobs nuevos (no termina el actual, solo no toma el próximo)
kill -USR2 $(cat tmp/pids/resque.pid)

# CONT: vuelve a tomar jobs
kill -CONT $(cat tmp/pids/resque.pid)

Este par es el equivalente Resque del Sidekiq::ProcessSet#quiet! que aparece en la parte 3 de la serie Sidekiq. Útil para deploy: USR2 en todos los workers, esperás que los jobs en curso terminen, hacés el deploy, CONT para reanudar.

Último recurso (KILL)

1
kill -9 $(cat tmp/pids/resque.pid)

KILL (-9) mata el worker y deja huérfano a cualquier hijo en ejecución — esos hijos se convierten en procesos zombie hasta que el init los recolecte, y el job en ejecución desaparece sin convertirse en failure (porque el worker no tuvo tiempo de registrarlo). Usá solo cuando:

  • QUIT se trabó y no responde
  • El worker está consumiendo memoria sin hacer nada visible
  • Hay un incendio y derribarlo es más importante que preservar el estado

Resumen de señales

Señal Efecto
QUIT Termina los jobs en curso, luego sale (graceful)
TERM Mata al hijo en el momento y sale
USR1 Mata al hijo en el momento, mantiene el worker corriendo
USR2 Deja de tomar jobs nuevos (pause)
CONT Vuelve a tomar jobs (resume)
KILL (-9) Mata el worker, deja hijos huérfanos

Próximo en la serie

Diagnóstico de Resque por la consolaResque.info, conteos de colas, listado de workers y qué está haciendo cada uno.

Esta entrada está licenciada bajo CC BY 4.0 por el autor.