2009-08-20 5 views
52

Ich habe Django eingerichtet, um einige wiederkehrende Aufgaben in ihren eigenen Threads auszuführen, und ich bemerkte, dass sie immer noch nicht abgeschlossene Datenbankverbindungsprozesse zurückließen (pgsql "Idle In Transaction").Threaded Django-Task verarbeitet Transaktionen oder Datenbankverbindungen nicht automatisch?

Ich schaute die Postgres-Protokolle durch und stellte fest, dass die Transaktionen nicht abgeschlossen wurden (kein ROLLBACK). Ich habe versucht, die verschiedenen Transaktionsdekoratoren auf meine Funktionen zu verwenden, kein Glück.

Ich wechselte auf manuelle Transaktionsverwaltung und führte das Rollback manuell durch, das hat funktioniert, aber die Prozesse immer noch als "Leerlauf" verlassen.

Also dann rief ich connection.close(), und alles ist gut.

Aber ich frage mich, warum funktioniert Djangos typisches Transaktions- und Verbindungsmanagement nicht für diese Thread-Aufgaben, die aus dem Haupt-Django-Thread hervorgehen?

Antwort

93

Nach Wochen des Testens und Lesen des Quellcodes Django, habe ich die Antwort auf meine eigene Frage gefunden:

Transaktionen

Djangos Standard noch autocommit Verhalten für mein Gewinde Funktion gilt. Allerdings heißt es in der Dokumentation Django:

Sobald Sie eine Aktion ausführen, um die Datenbank schreiben muss, erzeugt Django die INSERT/UPDATE/DELETE-Anweisungen und dann wird der COMMIT. Es gibt kein implizites ROLLBACK.

Dieser letzte Satz ist sehr wörtlich. Es gibt keinen ROLLBACK-Befehl, es sei denn, etwas in Django hat das schmutzige Flag gesetzt. Da meine Funktion nur SELECT-Anweisungen ausführte, hat sie nie das Dirty-Flag gesetzt und kein COMMIT ausgelöst.

Das widerspricht der Tatsache, dass PostgreSQL denkt, dass die Transaktion ein ROLLBACK erfordert, weil Django einen SET-Befehl für die Zeitzone ausgegeben hat. Bei der Durchsicht der Logs warf ich mich ab, weil ich diese ROLLBACK-Statements immer wieder sah und annahm, dass Djangos Transaktionsverwaltung die Quelle war. Stellt sich heraus, ist es nicht, und das ist in Ordnung.

Verbindungen

Die Verbindungsverwaltung ist, wo die Dinge tun knifflig. Es stellt sich heraus, dass Django signals.request_finished.connect(close_connection) verwendet, um die normalerweise verwendete Datenbankverbindung zu schließen. Da in Django normalerweise nichts passiert, was keine Anfrage beinhaltet, ist dieses Verhalten selbstverständlich.

In meinem Fall gab es jedoch keine Anfrage, weil der Job geplant war. Keine Anfrage bedeutet kein Signal. Kein Signal bedeutet, dass die Datenbankverbindung nie geschlossen wurde.

Zurück zu den Transaktionen, es stellt sich heraus, dass die einfache Ausgabe eines Aufrufs an connection.close() in Abwesenheit von Änderungen an der Transaktionsverwaltung die ROLLBACK-Anweisung im PostgreSQL-Protokoll, das ich gesucht habe.

Lösung

Die Lösung ist die normale Django Transaktionsmanagement als normal ablaufen zu lassen und einfach die Verbindung eine von drei Arten schließen:

  1. einen Dekorateur schreiben, die die Verbindung schließt und wickle die notwendigen Funktionen darin ein.
  2. Haken Sie die bestehenden Anforderungssignale an, damit Django die Verbindung schließt.
  3. Schließen Sie die Verbindung manuell am Ende der Funktion.

Alle diese drei werden (und tun).

Das hat mich wochenlang verrückt gemacht. Ich hoffe, das hilft einem anderen in der Zukunft!

+1

Froh, dass Sie das endlich gelöst haben. Es war nicht offensichtlich. Ich frage mich, ob es ein Ticket wert sein könnte, irgendwo in den Dokumenten eine Notiz hinzuzufügen. –

+1

Dieses Ticket beschreibt ein sehr ähnliches Problem: http://code.djangoproject.com/ticket/9964 – zooglash

+0

Wow. eine Chance, dass Sie Code teilen können? – Dejell

Verwandte Themen