2016-03-17 5 views
7

Ich laufe immer wieder in seltsame mysql-Probleme, während Arbeiter Aufgaben direkt nach der Erstellung ausführen.Erstellen einer separaten Datenbankverbindung für jeden Sellerie-Arbeiter

Wir verwenden django 1.3, Sellerie 3.1.17, djorm-ext-pool 0,5

Wir beginnen Sellerie Prozess mit Concurrency 3. Mein obeservation weit so ist, dann, wenn der Arbeitnehmer Prozessstart, alle gleiche mysql erhalten Verbindung. Wir protokollieren die Db-Verbindungs-ID wie folgt.

from django.db import connection 
connection.cursor() 
logger.info("Task %s processing with db connection %s", str(task_id), str(connection.connection.thread_id())) 

Wenn alle Arbeiter Aufgaben erhalten, wird die erste erfolgreich ausgeführt, aber die anderen zwei ergeben seltsame Mysql-Fehler. Es sind entweder Fehler mit "Mysql Server weggegangen", oder mit einer Bedingung, wo Django "DoesNotExist" Fehler wirft. eindeutig existieren die Objekte, die Django abfragt.

Nach diesem Fehler beginnt jeder Worker, eine eigene Datenbankverbindung zu erhalten, nach der wir kein Problem finden.

Wie ist das Standardverhalten von Sellerie? Ist es entworfen, um die gleiche Datenbankverbindung zu teilen. Wenn ja, wie wird die Kommunikation zwischen den Prozessen gehandhabt? Ich würde idealerweise unterschiedliche Datenbankverbindung für jeden Arbeiter bevorzugen.

Ich versuchte den unten genannten Code, der nicht funktioniert hat. Celery Worker Database Connection Pooling

Wir haben auch den unten empfohlenen Sellerie-Code korrigiert. https://github.com/celery/celery/issues/2453

Für diejenigen, die die Frage herunterschwenken, lassen Sie mich bitte den Grund für den Downvote wissen.

+0

Verwenden Sie eine Django-Verbindungspooling-Middleware? Was ist deine 'CONN_MAX_AGE' in der Django-Konfiguration? Ich denke, das beeinflusst das dauerhafte Verbindungsverhalten in Django. Dies könnte mit dem Verhalten zusammenhängen, das Sie sehen, nicht mit Sellerie. –

+0

Konnten Sie einfach concurrency = 1 ausführen und mehrere Worker starten? –

+0

@AlexLuisArias Das würde nur einen Worker-Prozess ausführen und ist kein Fall für das obige Problem. –

Antwort

2

Sellerie wird mit folgendem Befehl

celery -A myproject worker --loglevel=debug --concurrency=3 -Q testqueue 

myproject.py im Rahmen des Master-Prozesses machte einige Abfragen zu MySQL-Datenbank, bevor Forking die Worker-Prozesse gestartet.

Als Teil des Abfrageflusses im Hauptprozess erstellt django ORM einen SQL-Verbindungspool, falls noch nicht vorhanden. Arbeiterprozesse werden dann erstellt.

Sellerie als Teil von Django-Fixups schließt bestehende Verbindungen.

def close_database(self, **kwargs): 
    if self._close_old_connections: 
     return self._close_old_connections() # Django 1.6 
    if not self.db_reuse_max: 
     return self._close_database() 
    if self._db_recycles >= self.db_reuse_max * 2: 
     self._db_recycles = 0 
     self._close_database() 
    self._db_recycles += 1 

In der Tat, was passieren könnte, ist, dass, wird das sqlalchemy Pool-Objekt mit einer nicht verwendeter DB-Verbindung zu dem Prozess 3 Arbeiter kopiert, wenn gegabelt. Die 3 verschiedenen Pools haben also 3 Verbindungsobjekte, die auf den gleichen Verbindungsdateideskriptor zeigen.

Arbeiter während der Ausführung der Aufgaben, wenn nach einer Db-Verbindung gefragt, erhalten alle Arbeiter die gleiche unbenutzte Verbindung von Sqlalchemy Pool, weil das derzeit nicht verwendet wird. Die Tatsache, dass alle Verbindungen auf den gleichen Dateideskriptor verweisen, hat die MySQL-Verbindungsfehler verursacht.

Neue Verbindungen, die danach erstellt werden, sind alle neu und zeigen nicht auf den gleichen Socket-Dateideskriptor.

Lösung:

Im Hauptprozess

from django.db import connection 
connection.cursor() 

hinzufügen, bevor eine Import abgeschlossen ist. d. h., bevor sogar das Modul djorm-ext-pool hinzugefügt wird.

Auf diese Weise alle DB-Abfragen verwenden Verbindung erstellt von Django außerhalb des Pools. Wenn Sellerie-Django-Reparatur die Verbindung schließt, wird die Verbindung tatsächlich geschlossen, anstatt zum Alchemie-Pool zurückzukehren und den Alchemie-Pool ohne Verbindungen zu verlassen, wenn alle Arbeiter bei der Verzweigung überfordert sind. Danach, wenn Arbeiter nach db-Verbindung fragen, gibt sqlalchemy eine der neu erzeugten Verbindungen zurück.

Verwandte Themen