2014-04-03 15 views
5

Ho un lavoratore sidekiq che non dovrebbe richiedere più di 30 secondi, ma dopo alcuni giorni scoprirò che l'intera coda di lavoro si arresta perché tutti gli operai sono rinchiusiLavoratore sidekiq in esecuzione per migliaia di secondi anche se c'è un timeout

Ecco il mio operaio:

class MyWorker 
    include Sidekiq::Worker 
    include Sidekiq::Status::Worker 
    sidekiq_options queue: :my_queue, retry: 5, timeout: 4.minutes 

    sidekiq_retry_in do |count| 
    5 
    end 

    sidekiq_retries_exhausted do |msg| 
    store({message: "Gave up."}) 
    end 

    def perform(id) 
    begin 
     Timeout::timeout(3.minutes) do 
     got_lock = with_semaphore("lock_#{id}") do 
      # DO WORK 
     end 
     end 
    rescue ActiveRecord::RecordNotFound => e 
     # Handle 
    rescue Timeout::Error => e 
     # Handle 
     raise e 
    end 
    end 

    def with_semaphore(name, &block) 
    Semaphore.get(name, {stale_client_timeout: 1.minute}).lock(1, &block) 
    end 
end 

E la classe del semaforo che usiamo. (Redis-semaforo gemma)

class Semaphore 
    def self.get(name, options = {}) 
    Redis::Semaphore.new(name.to_sym, 
     :redis => Application.redis, 
     stale_client_timeout: options[:stale_client_timeout] || 1.hour, 
    ) 
    end 
end 

Fondamentalmente mi fermo l'operaio e sarà stato di fatto: 10000 secondi, che il lavoratore non dovrebbe mai essere in esecuzione per.

Qualcuno ha qualche idea su come risolvere questo o quello che lo sta causando? I lavoratori sono in esecuzione su EngineYard.

Modifica: un commento aggiuntivo. Il # DO WORK ha la possibilità di attivare una funzione PostgresSQL. Ho notato nei log alcuni riferimenti a PG :: TRDeadlockDetected: ERRORE: deadlock rilevato. Questo potrebbe causare il mancato completamento dell'operatore anche con un timeout impostato?

+0

C'è un motivo specifico per cui stai usando un blocco Semaforo all'interno metodo "eseguire" del lavoratore? Lo sto chiedendo perché sento che questo mix di Sidekiq + Locking è un po 'pericoloso. [I popoli Sidekiq raccomandano] (https://github.com/mperham/sidekiq/wiki/Best-Practices) i lavori devono essere il più possibile isolati, quindi non ci sono colli di bottiglia o potenziali deadlock che possono verificarsi bloccando le cose durante #perform –

+0

In combinazione con sidetiq, noto che a volte lo stesso ID viene gettato sulla coda per essere elaborato, questo impedisce che venga elaborato due volte. – Geesu

risposta

1

Dato che si desidera garantire l'esecuzione del lavoro unico, vorrei tentare di rimuovere tutti i blocchi e delegare il lavoro di controllo unicità ad un plugin come Sidekiq Unique Jobs

In questo caso, anche se sidetiq Enqueue lo stesso lavoro id due volte, questo plugin assicura sarà accodato/elaborato una sola volta.

+0

Quindi credi che il problema sia redis-semaphore? Non vedo ancora perché il timeout verrebbe ignorato. – Geesu

0

Si potrebbe anche provare l'ActiveRecord with_lock meccanismo: http://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html

+0

In realtà sospetto che questa possa essere la vera causa del problema: http://blog.headius.com/2008/02/rubys-threadraise-threadkill-timeoutrb.html – Geesu

+0

Ah, sì, sembra molto simile. Nota per self, non eseguire il timeout con un blocco del database. C'è una ragione per cui hai bisogno di uscire? Forse c'è un modo per rompere la logica per consentire l'accesso al record in mezzo. – lobati

+0

btw, dovresti considerare di rispondere alla tua stessa domanda. – lobati