Отладка livelock в Джанго /Postgresql
Я запускаю умеренно популярное веб-приложение на Django с Apache2, mod_python и PostgreSQL 8.3 с базой данных базы данных postgresql_psycopg2. Я иногда испытываю живую блокировку, которую можно определить, когда процесс apache2 постоянно потребляет 99% ЦП в течение нескольких минут или более.
Я выполнил pac strace -p для процесса apache2 и обнаружил, что он постоянно повторяет эти системные вызовы:
sendto(25, "Q\0\0\0SSELECT (1) AS \"a\" FROM \"account_profile\" WHERE \"account_profile\".\"id\" = 66201 \0", 84, 0, NULL, 0) = 84
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
poll([{fd=25, events=POLLIN|POLLERR, revents=POLLIN}], 1, -1) = 1
recvfrom(25, "E\0\0\0\210SERROR\0C25P02\0Mcurrent transaction is aborted, commands ignored until end of transaction block\0Fpostgres.c\0L906\0Rexec_simple_query\0\0Z\0\0\0\5E", 16384, 0, NULL, NULL) = 143
Этот точный фрагмент постоянно повторяется в трассировке и работал в течение более 10 минут, прежде чем я окончательно убил процесс apache2. (Примечание: я отредактировал это, чтобы заменить мой предыдущий фрагмент strace новым, который полностью отображает содержимое строки, а не обрезается.)
Моя интерпретация вышеизложенного состоит в том, что django пытается выполнить проверку существования моей таблицы account_profile, но в какой-то более ранний момент (до того, как я начал трассировку) что-то пошло не так (ошибка синтаксического анализа SQL? Нарушение целостности ссылок или ограничения уникальности? Кто знает?), и теперь Postgresql возвращает ошибку "текущая транзакция прервана". По какой-то причине вместо того, чтобы вызвать исключение и сдаться, он просто повторяет попытку.
Одна возможность состоит в том, что это вызывается при вызове Profile.objects.get_or_create. Это класс модели, который отображается в таблицу account_profile. Возможно, в get_or_create есть что-то, предназначенное для перехвата слишком широкого набора исключений и повторных попыток? Из журналов веб-сервера видно, что эта живая блокировка могла произойти в результате двойного щелчка по кнопке POST в форме регистрации моего сайта.
Это состояние встречалось пару раз за последние несколько дней на живом сайте и приводит к значительному замедлению, пока я не вмешаюсь, так что почти все, кроме бесконечного тупика, будет улучшением!:)
2 ответа
Это оказалось полностью моей ошибкой. Я нашел место, где select (1) as 'a'
заявление, казалось, возникло (в django/models/base.py
) и взломал его, чтобы записать трассировку, которая четко указывала на мой код.
У меня был некоторый код, который составляет уникальный электронный ключ для каждого профиля. Эти ключи генерируются случайным образом, поэтому, поскольку существует некоторая вероятность наложения, я запускаю их в попытке / за исключением цикла while. Я предполагал, что ограничение уникальности базы данных приведет к сбою сохранения, если ключ не будет уникальным, и я смогу повторить попытку.
К сожалению, в Postgresql вы не можете просто повторить попытку после ошибки целостности. Вы должны выполнить команду COMMIT или ROLLBACK (даже если вы, очевидно, находитесь в режиме автоматической фиксации), прежде чем сможете повторить попытку. Поэтому у меня был бесконечный цикл неудачных попыток сохранения, когда я игнорировал сообщение об ошибке.
Сейчас я ищу более конкретное исключение (django.db.IntegrityError
) и выполнить ограниченное количество попыток, чтобы цикл не был бесконечным.
Спасибо всем за просмотр / ответ.
Ваш анализ звучит довольно хорошо. Понятно, что дело не в том, что транзакция прервана. Я предлагаю вам сообщить об этом как об ошибке в проекте django...