Django + FastCGI - случайное повышение OperationalError

Я запускаю приложение 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.
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
8
0
11 025
13
Перейти к ответу Данный вопрос помечен как решенный

Ответы 13

В коммутаторе вы меняли версии клиент / сервер PostgreSQL?

Я видел похожие проблемы с php + mysql, и виновником была несовместимость между версиями клиент / сервер (даже если у них была одна и та же основная версия!)

Нет. Я только переключил Apache + mod_python на Lighttpd + FastCGI.

ibz 26.12.2008 12:48

Пахнет возможной проблемой заправки ниток. Django - это гарантированная потокобезопасность нет, хотя внутрифайловая документация, похоже, указывает, что Django / FCGI может быть запущен таким образом. Попробуйте запустить с prefork, а затем выбейте дерьмо из сервера. Если проблема исчезнет ...

Возможно, переменная среды PYTHONPATH и PATH отличается для обеих настроек (Apache + mod_python и lighttpd + FastCGI).

Ответ принят как подходящий

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

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

Само по себе это не решает проблемы. Просто обходит стороной. : P Может быть, не вариант для некоторых.

pkoch 22.07.2010 18:40

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

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

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

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

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

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

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

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

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

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

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

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

Переход с method = prefork на method = threadaded решил проблему для меня.

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

Until recently I was curious to test this on Django 1.1.1. Will this exception be thrown again... surprise, there it was again. It took me some time to debug this, helpful hint was that it only shows when (pre)forking. So for those who getting randomly those exceptions, I can say... fix your code :) Ok.. seriously, there are always few ways of doing this, so let me firs explain where is a problem first. If you access database when any of your modules will import as, e.g. reading configuration from database then you will get this error. When your fastcgi-prefork application starts, first it imports all modules, and only after this forks children. If you have established db connection during import all children processes will have an exact copy of that object. This connection is being closed at the end of request phase (request_finished signal). So first child which will be called to process your request, will close this connection. But what will happen to the rest of the child processes? They will believe that they have open and presumably working connection to the db, so any db operation will cause an exception. Why this is not showing in threaded execution model? I suppose because threads are using same object and know when any other thread is closing connection. How to fix this? Best way is to fix your code... but this can be difficult sometimes. Other option, in my opinion quite clean, is to write somewhere in your application small piece of code:

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/local/lib/python2.6/dist-packages/Django-1.1.1-py2.6.egg/django/db/__init__.py

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

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

/usr/local/lib/python2.6/dist-packages/Django-1.1.1-py2.6.egg/django/db/backends/__init__.py

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

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

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

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

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

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

Применимая цитата:

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

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

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

Другие вопросы по теме