После нескольких недель тестирования и чтения исходного кода Django я нашел ответ на свой вопрос:
Сделка
Поведение Django по умолчанию при автоматической фиксации сохраняется для моей многопоточной функции. Однако в документации Django говорится:
Как только вы выполняете действие, которое необходимо записать в базу данных, Django создает операторы INSERT / UPDATE / DELETE и затем выполняет COMMIT. Там нет неявного ROLLBACK.
Последнее предложение очень буквально. Он НЕ выполняет команду ROLLBACK, если что-то в Django не установило грязный флаг. Так как моя функция выполняла только операторы SELECT, она никогда не устанавливала грязный флаг и не вызывала COMMIT.
Это противоречит тому факту, что PostgreSQL считает, что транзакция требует ROLLBACK, потому что Django выдал команду SET для часового пояса. Просматривая журналы, я отбросил себя, потому что я продолжал видеть эти операторы ROLLBACK и предполагал, что управление транзакциями в Django было источником. Оказывается, это не так, и это нормально.
Соединения
Управление соединениями - вот где все становится сложнее. Оказывается, Django использует signals.request_finished.connect(close_connection)
, чтобы закрыть соединение с базой данных, которое оно обычно использует. Поскольку в Django обычно не происходит ничего, что не связано с запросом, вы воспринимаете это как должное.
В моем случае, однако, не было никакого запроса, потому что работа была запланирована. Нет запроса означает отсутствие сигнала. Отсутствие сигнала означает, что соединение с базой данных никогда не было закрыто.
Возвращаясь к транзакциям, получается, что простой вызов connection.close()
при отсутствии каких-либо изменений в управлении транзакциями вызывает инструкцию ROLLBACK в журнале PostgreSQL, который я искал.
Решение
Решение состоит в том, чтобы позволить нормальному управлению транзакциями Django продолжать работу в обычном режиме и просто закрыть соединение одним из трех способов:
- Напишите декоратор, который закрывает соединение и включает в него необходимые функции.
- Подключите существующие сигналы запроса, чтобы Django закрыл соединение.
- Закройте соединение вручную в конце функции.
Любой из этих трех будет (и будет) работать.
Это сводило меня с ума на несколько недель. Я надеюсь, что это поможет кому-то еще в будущем!