2015-06-06 13 views
8

Ich benutze Sidekiq in meiner Rails App, um 50k + Jobs auf einmal in Warteschlange zu stellen. Unsere Poolgröße ist auf 9 gesetzt.Nebenläufigkeit mit Sidekiq verursacht einige Probleme

Die Jobs sind alle verwandt und tun das gleiche. Wir haben ein anderes Modell, das einen Zähler hat. Während jedes Jobs überprüfen wir, ob dieses Modell eine Spalte mit einem Wert über 200 aufweist. Wenn es über 200 liegt, erstellen wir eine weitere Instanz dieses Modells mit Wert = 0 und setzen die Jobs fort. Da jedoch 9 Jobs gleichzeitig ausgeführt werden, lesen alle 9 Jobs den Wert dieser Spalte gleichzeitig mit mehr als 200, und alle erstellen neue Instanzen, was nicht richtig ist.

Was ist die beste Lösung für dieses Problem? Wir möchten grundsätzlich, dass alle Jobs vom aktuellsten Wert gelesen werden.

+0

Also, wie ich Ihre Arbeitsplätze undrrestand tun einige Mitarbeiter, UND jedes Mal Wert überprüfen und neues Modell erstellen? Vielleicht können Sie einen separaten Job für diese Mitarbeiter erstellen? 8 Jobs machen nur ihre Arbeit, und ein Job überprüft nur diesen Wert und erstellt eine neue Instanz von benötigt? – denys281

+0

Jeder Job prüft einen Wert in der Datenbank und stellt nur gelegentlich fest, dass er ein neues Modell erstellen muss. Wenn dies jedoch geschieht, müssen alle derzeit ausgeführten Jobs ein neues Modal auf einmal erstellen und 8-10 davon erstellen. Es sollte nur 1 erstellen. –

Antwort

1

Ich kann keinen spezifischen Code veröffentlichen, da dies stark von Ihrem Datenbanktyp und den Einstellungen abhängt, aber Sie sollten versuchen, die Datenbank zu sperren.

Worker beim Lesen der Tabelle sollte es sperren, bis es mit dem Erstellen eines neuen Datensatzes mit Wert 0 fertig ist. Sie sollten die Tabelle zum Lesen sperren, damit andere Arbeiter warten müssen, bis dieser eine Worker fertig ist. Es ist auch möglich, separate Zeilen zu sperren, aber ich weiß nicht, ob es in Ihrem Fall funktioniert.

1

Angenommen, Ihr Modell heißt Counter.

Zuerst finden/erstellen einen entsprechenden Counter:

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

(Anmerkung: Dies ist nicht atomar Wenn Sie nicht mehr als 1 aktive Zähler haben können dann sehen:
Race conditions in Rails first_or_create und
How do I avoid a race condition in my Rails app?)

Als nächstes versuchen es zu erhöhen atomar im DB:

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

Wenn das funktionierte, success == 1 sonst success == 0. Wenn es funktioniert, verwenden Sie den Zähler, andernfalls versuchen Sie es erneut.

1

Redis ist besser für diese Art von Betrieb geeignet und Sie haben bereits einfachen Zugriff darauf über Sidekiqs Redis-Verbindung.

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

Vielleicht würde so etwas für Sie arbeiten.

+0

Dies scheint wie eine gute, einfache Lösung. Nachdem jedoch einer der Jobs einen Wert> 200 gelesen hat, muss er eine API eines Drittanbieters pingen und eine Antwort erhalten. Wie kann ich während dieser Zeit die anderen Jobs warten lassen, bis diese abgeschlossen sind? –

+0

Sidekiq-Jobs sind nicht zum Koordinieren vorgesehen. Es gibt keine einfache Lösung für Ihre Frage. –

+0

Hey @mikeperham - wie wäre es mit dieser Lösung: http://stackoverflow.com/questions/10750626/transactions-and-watch-statement-in-redis –

Verwandte Themen