2015-06-06 13 views
8

Sto usando Sidekiq nella mia app per rotaie per accodare 50k + lavori alla volta. La dimensione della piscina è impostata su 9.La concorrenza con Sidekiq sta causando alcuni problemi

I lavori sono tutti correlati e fanno la stessa cosa. Abbiamo un altro modello che ha un contatore su di esso. Durante ogni lavoro, controlliamo se quel modello ha una colonna con valore superiore a 200. Se è superiore a 200, creiamo un'altra istanza di quel modello con valore = 0 e proseguo con i lavori. Tuttavia, poiché abbiamo 9 lavori in esecuzione alla volta, tutti e 9 i lavori leggono il valore di quella colonna per essere maggiore di 200 allo stesso tempo e tutti creano nuove istanze, il che non è corretto.

Qual è il modo migliore per risolvere questo problema? Fondamentalmente vogliamo che tutti i lavori leggano dal valore più aggiornato.

+0

Quindi, mentre svolgo il lavoro svolto da personale, E ogni volta controllo il valore e creo un nuovo modello? Forse puoi creare un lavoro separato per questo personale? 8 lavori fanno il loro lavoro e un lavoro controlla solo questo valore e crea una nuova istanza del necessario? – denys281

+0

Ogni lavoro verifica un valore nel DB e solo una volta ogni tanto scopre che è necessario creare un nuovo modello. Tuttavia, quando succede, tutti i lavori attualmente in esecuzione scoprono che è necessario creare una nuova modal in una volta e crearne 8-10. Dovrebbe creare solo 1. –

risposta

1

Non riesco a inserire alcun codice specifico perché dipenderà molto dal tipo di database e dalle impostazioni, ma dovresti provare il blocco del database.

Il lavoratore durante la lettura della tabella deve bloccarlo fino a quando non termina con la creazione di un nuovo record con valore 0. È necessario bloccare la tabella per la lettura in modo che altri lavoratori debbano attendere fino alla fine di questo lavoratore. È anche possibile bloccare righe separate, ma non so se funzionerà nel tuo caso.

1

Supponiamo che il modello sia chiamato Counter.

primo luogo, trovare/creare un contatore appropriato:

counter = Counter.where('count < 200').first_or_create ...

(Nota: questo non è atomica Se non è possibile avere più di 1 contatore attivo allora vedere:
Race conditions in Rails first_or_create e
How do I avoid a race condition in my Rails app?)

Avanti, cercano ad incrementarlo atomicamente nel DB:

success = 
    Counter.where(id: counter.id) 
     .where('count < 200') 
     .update_all('count = count + 1') 

Se ciò ha funzionato, success == 1 altrimenti success == 0. Se ha funzionato, usa il contatore, altrimenti riprova.

1

Redis è più adatto per questo tipo di operazione e si dispone già di un facile accesso tramite la connessione Redis di Sidekiq.

value = Sidekiq.redis { |c| c.incr("my-counter") } 
if value % 200 == 0 
    # create new instance 
end 

Forse qualcosa del genere potrebbe funzionare per voi.

+0

Questa sembra una soluzione buona e semplice. Tuttavia, dopo che uno dei lavori ha letto un valore> 200, è necessario eseguire il ping di un'API di terze parti e ottenere una risposta. Durante questo periodo, come faccio ad aspettare che gli altri lavori siano completati? –

+0

I lavori sidekiq non sono progettati per il coordinamento. Non c'è una soluzione semplice alla tua domanda. –

+0

Hey @mikeperham - che ne dici di questa soluzione: http://stackoverflow.com/questions/10750626/transactions-and-watch-statement-in-redis –

Problemi correlati