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:
- einen Dekorateur schreiben, die die Verbindung schließt und wickle die notwendigen Funktionen darin ein.
- Haken Sie die bestehenden Anforderungssignale an, damit Django die Verbindung schließt.
- 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!
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. –
Dieses Ticket beschreibt ein sehr ähnliches Problem: http://code.djangoproject.com/ticket/9964 – zooglash
Wow. eine Chance, dass Sie Code teilen können? – Dejell