Многопоточная задача Django не обрабатывает автоматически транзакции или соединения БД? - PullRequest
53 голосов
/ 20 августа 2009

Я настроил Django для запуска некоторых повторяющихся задач в своих собственных потоках, и я заметил, что они всегда оставляют позади незавершенные процессы подключения к базе данных (pgsql "Idle In Transaction").

Я просмотрел журналы Postgres и обнаружил, что транзакции не были завершены (без ROLLBACK). Я попытался использовать различные декораторы транзакций в своих функциях, но не повезло.

Я переключился на ручное управление транзакциями и сделал откат вручную, что сработало, но все же оставило процессы как «бездействующие».

Итак, я позвонил connection.close (), и все хорошо.

Но мне интересно, почему обычное управление транзакциями и соединениями в Django не работает для этих многопоточных задач, порождаемых из основного потока Django?

1 Ответ

100 голосов
/ 28 августа 2009

После нескольких недель тестирования и чтения исходного кода 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 продолжать работу в обычном режиме и просто закрыть соединение одним из трех способов:

  1. Напишите декоратор, который закрывает соединение и включает в него необходимые функции.
  2. Подключите существующие сигналы запроса, чтобы Django закрыл соединение.
  3. Закройте соединение вручную в конце функции.

Любой из этих трех будет (и будет) работать.

Это сводило меня с ума на несколько недель. Я надеюсь, что это поможет кому-то еще в будущем!

...