2012-04-07 16 views
5

Mi aplicación crea trabajos resque que se deben procesar secuencialmente por usuario, y deben procesarse lo más rápido posible (1 segundo de retraso máximo).Resque: trabajos críticos para el tiempo que se ejecutan secuencialmente por usuario

Un ejemplo: job1 y job2 se crean para user1 y job3 para user2. Resque puede procesar job1 y job3 en paralelo, pero job1 y job2 deben procesarse secuencialmente.

que tienen diferentes pensamientos para una solución:

  • que podría utilizar diferentes colas (por ejemplo queue_1 ... queue_10) y empezar un trabajador por cada cola (por ejemplo rake resque:work QUEUE=queue_1). Los usuarios se asignan a una cola/trabajador en tiempo de ejecución (por ejemplo, al iniciar sesión, todos los días, etc.)
  • Podría usar "colas de usuario" dinámicas (por ejemplo, cola _ # {user.id}) e intentar extender resque que solo 1 trabajador puede procesar una cola a la vez (como se solicitó en Resque: one worker per queue)
  • Podía poner los trabajos en una cola sin interrupción y usar un "meta trabajo por usuario" con resque-lock (https://github.com/defunkt/resque-lock) que maneja esos trabajos.

¿Tiene alguna experiencia con uno de esos escenarios en la práctica? ¿O tiene otras ideas que valdría la pena pensar? Agradecería cualquier aporte, ¡gracias!

Respuesta

5

Gracias a la respuesta de @Isotope Finalmente llegué a una solución que parece funcionar (usando resque-reintento y cerraduras en Redis:

class MyJob 
    extend Resque::Plugins::Retry 

    # directly enqueue job when lock occurred 
    @retry_delay = 0 
    # we don't need the limit because sometimes the lock should be cleared 
    @retry_limit = 10000 
    # just catch lock timeouts 
    @retry_exceptions = [Redis::Lock::LockTimeout] 

    def self.perform(user_id, ...) 
    # Lock the job for given user. 
    # If there is already another job for the user in progress, 
    # Redis::Lock::LockTimeout is raised and the job is requeued. 
    Redis::Lock.new("my_job.user##{user_id}", 
     :expiration => 1, 
     # We don't want to wait for the lock, just requeue the job as fast as possible 
     :timeout => 0.1 
    ).lock do 
     # do your stuff here ... 
    end 
    end 
end 

estoy usando aquí Redis :: Bloqueo de https://github.com/nateware/redis-objects (que encapsula el patrón de http://redis.io/commands/setex).

2

He hecho esto antes.

La mejor solución para garantizar secuencialmente para cosas como esta es tener el final de la cola de trabajo1 en el trabajo2. job1's y job2's pueden ir en la misma cola o diferentes colas, no importará secuencialmente, depende de usted.

Cualquier otra solución, como poner en cola jobs1 + 2 al mismo tiempo, pero decir a job2 que comience en 0.5secs daría como resultado condiciones de carrera, por lo que no es recomendable.

Tener job1 trigger job2 también es realmente fácil de hacer.

Si quiere otra opción por el mero hecho de hacerlo: Mi última sugerencia sería agrupar ambos trabajos en un solo trabajo y agregar un parámetro para si la segunda parte también debe activarse.

p. Ej.

def my_job(id, etc, etc, do_job_two = false) 
    ...job_1 stuff... 
    if do_job_two 
    ...job_2 stuff... 
    end 
end 
+0

el problema es que no debe job1 explícitamente saber sobre job2 ya que no se crean al mismo tiempo (que podrían creado en diferentes solicitudes, etc.). – lacco

+0

en cuyo caso usaría resque-retry, en job2, para realizar una comprobación si el trabajo1 ha finalizado y si no se vuelve a cola más adelante, se puede configurar resque-retry para que use retrocesos exponenciales o intentos limitados según sea necesario. – TomDunning

+0

¿Cómo sabe job2 que el trabajo1 está en progreso para un usuario específico? – lacco

Cuestiones relacionadas