2012-04-07 14 views
5

La mia applicazione crea processi di resque che devono essere elaborati in sequenza per utente e devono essere elaborati il ​​più velocemente possibile (ritardo di 1 secondo).Resque: lavori time-critical eseguiti sequenzialmente per utente

Un esempio: job1 e job2 vengono creati per utente1 e job3 per utente2. Resque può elaborare job1 e job3 in parallelo, ma job1 e job2 devono essere elaborati in modo sequenziale.

ho pensieri diversi per una soluzione:

  • ho potuto utilizzare diverse code (ad esempio queue_1 ... queue_10) e avviare un lavoratore per ogni coda (ad esempio rake resque:work QUEUE=queue_1). Gli utenti vengono assegnati a una coda/lavoratore in fase di esecuzione (ad esempio all'accesso, ogni giorno ecc.)
  • Potrei usare "code utente" dinamiche (ad es. Queue _ # {user.id}) e provare ad estendere resque che solo 1 operatore può elaborare una coda alla volta (come richiesto in Resque: one worker per queue)
  • È possibile inserire i lavori in una coda non resque e utilizzare un "meta job per utente" con resque-lock (https://github.com/defunkt/resque-lock) che gestisce tali processi.

Hai qualche esperienza con uno di questi scenari nella pratica? Oppure hanno altre idee che potrebbero valere la pena di pensare? Gradirei qualsiasi input, grazie!

risposta

5

Grazie alla risposta di @Isotope ho finalmente arrivato a una soluzione che sembra funzionare (utilizzando resque-retry e si blocca in 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 

sto usando qui Redis :: Lock da https://github.com/nateware/redis-objects (che incapsula il modello da http://redis.io/commands/setex).

2

L'ho già fatto.

La migliore soluzione per garantire in modo sequenziale cose come questa è avere la fine del lavoro1 in coda al lavoro2. job1 e job2 possono quindi andare nella stessa coda o in code diverse, non importa per la sequenza, dipende da voi.

Qualsiasi altra soluzione, come l'accodamento dei processi1 + 2 allo stesso tempo MA dire che job2 deve iniziare in 0,5 secondi comporterebbe condizioni di gara, quindi non è consigliabile.

Avere job1 trigger job2 è anche molto facile da fare.

Se si desidera un'altra opzione per il gusto: il mio suggerimento finale sarebbe quello di raggruppare entrambi i lavori in un singolo lavoro e aggiungere un parametro per se anche la seconda parte dovrebbe essere attivata.

ad es.

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

il problema è che job1 non deve sapere esplicitamente di job2 dal momento che essi non sono creati allo stesso tempo (che potrebbe creati in diverse richieste, ecc). – lacco

+0

nel qual caso userei resque-retry, su job2, per eseguire un controllo se job1 è terminato e se non ri-accodare in seguito - resque-retry può essere impostato per utilizzare backoff esponenziali o trys limitati, se necessario. – TomDunning

+0

In che modo job2 sa che job1 è in corso per un utente specifico? – lacco

Problemi correlati