Django + FastCGI - случайное повышение OperationalError - PullRequest
8 голосов
/ 26 декабря 2008

Я использую приложение Django. Если раньше это было под Apache + mod_python, то все было в порядке. Переключился на Lighttpd + FastCGI. Теперь я случайно получаю следующее исключение (ни место, ни время, когда оно кажется предсказуемым). Поскольку он случайный и появляется только после переключения на FastCGI, я предполагаю, что он как-то связан с некоторыми настройками.

При поиске в Google найдено несколько результатов, но, похоже, они связаны с установкой maxrequests = 1. Тем не менее, я использую значение по умолчанию, равное 0.

Есть идеи, где искать?

PS. Я использую PostgreSQL. Может также быть связано с этим, так как исключение появляется при выполнении запроса к базе данных.

 File "/usr/lib/python2.6/site-packages/django/core/handlers/base.py", line 86, in get_response
   response = callback(request, *callback_args, **callback_kwargs)

 File "/usr/lib/python2.6/site-packages/django/contrib/admin/sites.py", line 140, in root
   if not self.has_permission(request):

 File "/usr/lib/python2.6/site-packages/django/contrib/admin/sites.py", line 99, in has_permission
   return request.user.is_authenticated() and request.user.is_staff

 File "/usr/lib/python2.6/site-packages/django/contrib/auth/middleware.py", line 5, in __get__
   request._cached_user = get_user(request)

 File "/usr/lib/python2.6/site-packages/django/contrib/auth/__init__.py", line 83, in get_user
   user_id = request.session[SESSION_KEY]

 File "/usr/lib/python2.6/site-packages/django/contrib/sessions/backends/base.py", line 46, in __getitem__
   return self._session[key]

 File "/usr/lib/python2.6/site-packages/django/contrib/sessions/backends/base.py", line 172, in _get_session
   self._session_cache = self.load()

 File "/usr/lib/python2.6/site-packages/django/contrib/sessions/backends/db.py", line 16, in load
   expire_date__gt=datetime.datetime.now()

 File "/usr/lib/python2.6/site-packages/django/db/models/manager.py", line 93, in get
   return self.get_query_set().get(*args, **kwargs)

 File "/usr/lib/python2.6/site-packages/django/db/models/query.py", line 304, in get
   num = len(clone)

 File "/usr/lib/python2.6/site-packages/django/db/models/query.py", line 160, in __len__
   self._result_cache = list(self.iterator())

 File "/usr/lib/python2.6/site-packages/django/db/models/query.py", line 275, in iterator
   for row in self.query.results_iter():

 File "/usr/lib/python2.6/site-packages/django/db/models/sql/query.py", line 206, in results_iter
   for rows in self.execute_sql(MULTI):

 File "/usr/lib/python2.6/site-packages/django/db/models/sql/query.py", line 1734, in execute_sql
   cursor.execute(sql, params)

OperationalError: server closed the connection unexpectedly
       This probably means the server terminated abnormally
       before or while processing the request.

Ответы [ 13 ]

4 голосов
/ 24 марта 2010

Возможное решение: http://groups.google.com/group/django-users/browse_thread/thread/2c7421cdb9b99e48

До недавнего времени мне было любопытно проверить это на Джанго 1.1.1. Будет ли это исключение быть брошенным снова ... сюрприз, там это было снова. Это заняло у меня немного время отладки, полезный совет был что он показывает только когда (предварительно) разветвление. Так что для тех, кто получает случайно эти исключения, я могу сказать ... исправить твой код :) окей .. серьезно, там всегда есть несколько способов сделать это, так позвольте мне объяснить, где находится проблема первая. Если вы получаете доступ к базе данных когда любой из ваших модулей будет импортировать как, например, чтение конфигурации из базы данных, то вы получите эту ошибку. Когда ваше приложение fastcgi-prefork запускается, сначала он импортирует все модули, и только после этого раздваивает детей. Если вы установили соединение БД во время импорта все дочерние процессы будет иметь точную копию этого объект. Эта связь в настоящее время закрыто в конце фазы запроса (сигнал запроса завершен). Итак, сначала ребенок, который будет призван к процессу ваш запрос, закроет это подключение. Но что будет с остальные дочерние процессы? Oни будет верить, что у них есть открытый и предположительно рабочая связь с дБ, поэтому любая операция дБ вызовет исключение. Почему это не отображается в модель исполнения с резьбой? Я полагаю потому что потоки используют один и тот же объект и знать, когда любой другой поток закрытие соединения. Как это исправить? Лучший способ исправить ваш код ... но это может быть сложно иногда. Другой вариант, на мой взгляд довольно чистый, это написать где-то в вашем небольшой кусочек кода приложения:

from django.db import connection 
from django.core import signals 
def close_connection(**kwargs): 
    connection.close() 
signals.request_started.connect(close_connection) 

Не идеальная мысль, в лучшем случае обход дважды - это обходной путь.


Возможное решение: использование пула соединений (pgpool, pgbouncer), чтобы у вас были соединения и стабильность соединений с БД, и они быстро передавались вашим демонам FCGI.

Проблема в том, что это вызывает другую ошибку, psycopg2 вызывает InterfaceError , потому что он пытается отключиться дважды (pgbouncer уже обработал это).

Теперь виновником является сигнал Django request_finished , запускающий connection.close () и вызывающий сбои, даже если он уже был отключен. Я не думаю, что такое поведение желательно, так как если запрос уже завершен, нас больше не заботит соединение с БД. Патч для исправления этого должен быть простым.

Соответствующая трассировка:

 /usr/local/lib/python2.6/dist-packages/Django-1.1.1-py2.6.egg/django/core/handlers/wsgi.py in __call__(self=<django.core.handlers.wsgi.WSGIHandler object at 0x24fb210>, environ={'AUTH_TYPE': 'Basic', 'DOCUMENT_ROOT': '/storage/test', 'GATEWAY_INTERFACE': 'CGI/1.1', 'HTTPS': 'off', 'HTTP_ACCEPT': 'application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate', 'HTTP_AUTHORIZATION': 'Basic dGVzdGU6c3VjZXNzbw==', 'HTTP_CONNECTION': 'keep-alive', 'HTTP_COOKIE': '__utma=175602209.1371964931.1269354495.126938948...none); sessionid=a1990f0d8d32c78a285489586c510e8c', 'HTTP_HOST': 'www.rede-colibri.com', ...}, start_response=<function start_response at 0x24f87d0>)
  246                 response = self.apply_response_fixes(request, response)
  247         finally:
  248             signals.request_finished.send(sender=self.__class__)
  249 
  250         try:
global signals = <module 'django.core.signals' from '/usr/local/l.../Django-1.1.1-py2.6.egg/django/core/signals.pyc'>, signals.request_finished = <django.dispatch.dispatcher.Signal object at 0x1975710>, signals.request_finished.send = <bound method Signal.send of <django.dispatch.dispatcher.Signal object at 0x1975710>>, sender undefined, self = <django.core.handlers.wsgi.WSGIHandler object at 0x24fb210>, self.__class__ = <class 'django.core.handlers.wsgi.WSGIHandler'>
 /usr/local/lib/python2.6/dist-packages/Django-1.1.1-py2.6.egg/django/dispatch/dispatcher.py in send(self=<django.dispatch.dispatcher.Signal object at 0x1975710>, sender=<class 'django.core.handlers.wsgi.WSGIHandler'>, **named={})
  164 
  165         for receiver in self._live_receivers(_make_id(sender)):
  166             response = receiver(signal=self, sender=sender, **named)
  167             responses.append((receiver, response))
  168         return responses
response undefined, receiver = <function close_connection at 0x197b050>, signal undefined, self = <django.dispatch.dispatcher.Signal object at 0x1975710>, sender = <class 'django.core.handlers.wsgi.WSGIHandler'>, named = {}
 /usr/local/lib/python2.6/dist-packages/Django-1.1.1-py2.6.egg/django/db/__init__.py in close_connection(**kwargs={'sender': <class 'django.core.handlers.wsgi.WSGIHandler'>, 'signal': <django.dispatch.dispatcher.Signal object at 0x1975710>})
   63 # when a Django request is finished.
   64 def close_connection(**kwargs):
   65     connection.close()
   66 signals.request_finished.connect(close_connection)
   67 
global connection = <django.db.backends.postgresql_psycopg2.base.DatabaseWrapper object at 0x17b14c8>, connection.close = <bound method DatabaseWrapper.close of <django.d...ycopg2.base.DatabaseWrapper object at 0x17b14c8>>
 /usr/local/lib/python2.6/dist-packages/Django-1.1.1-py2.6.egg/django/db/backends/__init__.py in close(self=<django.db.backends.postgresql_psycopg2.base.DatabaseWrapper object at 0x17b14c8>)
   74     def close(self):
   75         if self.connection is not None:
   76             self.connection.close()
   77             self.connection = None
   78 
self = <django.db.backends.postgresql_psycopg2.base.DatabaseWrapper object at 0x17b14c8>, self.connection = <connection object at 0x1f80870; dsn: 'dbname=co...st=127.0.0.1 port=6432 user=postgres', closed: 2>, self.connection.close = <built-in method close of psycopg2._psycopg.connection object at 0x1f80870>

Обработка исключений здесь может добавить больше снисхождения:

/ USR / местные / Библиотека / python2.6 / Расс-пакеты / Django-1.1.1-py2.6.egg / Джанго / дб / __ __ INIT. Ру

   63 # when a Django request is finished.
   64 def close_connection(**kwargs):
   65     connection.close()
   66 signals.request_finished.connect(close_connection)

Или это может быть лучше обработано на psycopg2, чтобы не выдавать фатальные ошибки, если все, что мы пытаемся сделать, это отключиться, и это уже так:

/ USR / местные / Библиотека / python2.6 / Расс-пакеты / Django-1.1.1-py2.6.egg / Джанго / дб / бэкэнды / __ __ INIT. Ру

   74     def close(self):
   75         if self.connection is not None:
   76             self.connection.close()
   77             self.connection = None

Кроме этого, у меня мало идей.

0 голосов
/ 09 марта 2019

Соответствующая цитата:

"2019 anyone?" - half of YouTube comments, circa 2019

Если кто-то все еще имеет дело с этим, убедитесь, что ваше приложение «нетерпеливо разветвляется» так, что ваш драйвер Python DB (psycopg2 для меня) не делит ресурсы между процессами.

Я решил эту проблему в uWSGI, добавив опцию lazy-apps = true, которая приводит к тому, что процессы приложения разветвляются прямо из шлюза, а не ожидают копирования при записи. Я полагаю, что другие хосты WSGI / FastCGI имеют аналогичные опции.

0 голосов
/ 14 октября 2015

Я пытаюсь дать ответ на это, даже если я использую не django, а пирамиду в качестве основы. Я столкнулся с этой проблемой в течение долгого времени. Проблема была в том, что эту ошибку было действительно трудно выдать для тестов ... Во всяком случае. Наконец, я решил эту проблему, перебирая весь материал сессий, областей видимости, экземпляры сессий, механизмы и соединения и т.д.

http://docs.sqlalchemy.org/en/rel_0_7/core/pooling.html#disconnect-handling-pessimistic

Этот подход просто добавляет слушателя в пул соединений движка. В слушателе статический выбор запрашивается к базе данных. Если он не работает, попробуйте установить новое соединение с базой данных, прежде чем он вообще потерпит неудачу. Важное замечание: Это происходит до того, как в базу данных будут добавлены другие данные. Таким образом, можно предварительно проверить соединение, что предотвращает сбой остальной части вашего кода.

Это не чистое решение, так как оно не решает саму ошибку, но работает как чудо. Надеюсь, это кому-нибудь поможет.

0 голосов
/ 06 марта 2010

Переход от метода = prefork к методу = многопоточный решил проблему для меня.

0 голосов
/ 07 ноября 2009

Почему бы не хранить сессию в кеше? Set

SESSION_ENGINE = "django.contrib.sessions.backends.cache"

Также вы можете попробовать использовать postgres с pgbouncer (postgres - сервер prefork и не любит много подключений / отключений за раз), но сначала проверьте ваш postgresql.log.

Другая версия - у вас много записей в таблицах сеансов, и очистка django-admin.py может помочь.

0 голосов
/ 16 ноября 2009

Проблема может быть в основном с импортом. По крайней мере, это то, что случилось со мной. Я написал свое собственное решение, не найдя ничего в Интернете. Пожалуйста, проверьте мой блог здесь: Простая утилита Python для проверки всех импортов в вашем проекте

Конечно, это поможет вам довольно быстро найти решение исходной проблемы, а не само решение вашей проблемы.

0 голосов
/ 29 апреля 2009

Рассматривали ли вы возможность перехода на Python 2.5.x (в частности, 2.5.4)? Я не думаю, что Django будет считаться зрелым на Python 2.6, поскольку есть некоторые несовместимые изменения. Однако я сомневаюсь, что это решит вашу проблему.

Кроме того, в Django 1.0.2 исправлены некоторые гнусные маленькие ошибки, поэтому убедитесь, что вы работаете с ними. Это очень хорошо может решить вашу проблему.

0 голосов
/ 27 июня 2009

Недавно я столкнулся с той же проблемой (lighttpd, fastcgi & postgre). Искал решение днями безуспешно и в крайнем случае переключился на mysql. Проблема исчезла.

0 голосов
/ 14 марта 2009

Я исправил аналогичную проблему при использовании модели геоджанго, которая не использовала ORM по умолчанию для одной из своих функций. Когда я добавил строку, чтобы вручную закрыть соединение, ошибка исчезла.

http://code.djangoproject.com/ticket/9437

Я все еще вижу ошибку случайным образом (~ 50% запросов), когда выполняю какие-либо действия при входе пользователя / сеансах.

0 голосов
/ 02 марта 2009

В итоге я переключился обратно на Apache + mod_python (у меня были другие случайные ошибки с fcgi, кроме этой), и теперь все хорошо и стабильно.

Вопрос все еще остается открытым. В случае, если кто-то столкнется с этой проблемой в будущем и решит ее, он может записать решение здесь для дальнейшего использования. :)

...