我一直在做一些测试,并且能够确认使用Django
withPostgreSQL
并且PGBouncer
它不会在失去连接时自动重新连接。老实说,我不确定这是一个错误还是设计使然。如果这是一个错误,我会很乐意报告它,如果不是,我想解释一下为什么以及如何绕过它,而不是另一个自定义后端。
通过不断地执行以下操作,我相当容易地完成了这些Django 1.8.4
测试Django 1.8.6
:
>>>Model.objects.all().count()
24
# Restart postgres with `sudo service postgres restart`
>>>Model.objects.all().count()
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py", line 318, in count
return self.query.get_count(using=self.db)
File "/usr/local/lib/python2.7/dist-packages/django/db/models/sql/query.py", line 466, in get_count
number = obj.get_aggregation(using, ['__count'])['__count']
File "/usr/local/lib/python2.7/dist-packages/django/db/models/sql/query.py", line 447, in get_aggregation
result = compiler.execute_sql(SINGLE)
File "/usr/local/lib/python2.7/dist-packages/django/db/models/sql/compiler.py", line 840, in execute_sql
cursor.execute(sql, params)
File "/usr/local/lib/python2.7/dist-packages/django/db/backends/utils.py", line 64, in execute
return self.cursor.execute(sql, params)
File "/usr/local/lib/python2.7/dist-packages/django/db/utils.py", line 98, in __exit__
six.reraise(dj_exc_type, dj_exc_value, traceback)
File "/usr/local/lib/python2.7/dist-packages/django/db/backends/utils.py", line 64, in execute
return self.cursor.execute(sql, params)
OperationalError: server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.
重新启动运行查询后等待多长时间都没关系,PostgreSQL
我仍然得到相同的结果。我看到Django
有代码可以运行 aSelect 1
来检查连接,但这似乎不起作用。回到Django 1.5.x
我们已经编写了一个自定义的 PostgreSQL 后端来做同样的事情,但删除了它,因为它似乎Django
在我们升级时内置了它。这是一个错误吗?
2015 年 11 月 6 日编辑:在 Django 中,PostgreSQL 后端实现了对数据库执行 a 的is_usable
函数。SELECT 1
但是,我无法找到is_usable
除 in 以外的任何用法,close_if_unusable_or_obsolete
但在任何地方都找不到它的任何用法。我认为它将在某些数据库包装器中使用,该包装器捕获异常并基于is_usable
.
上面提到的代码可以django/db/backends/postgresql_psychopg2/base.py
在 Django 1.8 中找到。
编辑 2 2015 年 11 月 6 日:好的,我为数据库编写了自己的自定义包装器,并且只是覆盖了ensure_connection
在连接丢失时尝试重新连接到数据库的方法。但是,在第一次尝试访问数据库进行会话时,我得到了另一个粗糙的回溯。但是,如果我立即再次查询它就可以了。如果我将我正在做的事情包装在一个try/except: pass
块中,它似乎一切正常,但不能真正判断它是否会在以后引起问题。这是回溯和代码。
>>> c = Model.objects.all().count()
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py", line 318, in count
return self.query.get_count(using=self.db)
File "/usr/local/lib/python2.7/dist-packages/django/db/models/sql/query.py", line 466, in get_count
number = obj.get_aggregation(using, ['__count'])['__count']
File "/usr/local/lib/python2.7/dist-packages/django/db/models/sql/query.py", line 447, in get_aggregation
result = compiler.execute_sql(SINGLE)
File "/usr/local/lib/python2.7/dist-packages/django/db/models/sql/compiler.py", line 838, in execute_sql
cursor = self.connection.cursor()
File "/usr/local/lib/python2.7/dist-packages/django/db/backends/base/base.py", line 164, in cursor
cursor = self.make_cursor(self._cursor())
File "/usr/local/lib/python2.7/dist-packages/django/db/backends/base/base.py", line 135, in _cursor
self.ensure_connection()
File "/usr/lib/python2.7/dist-packages/custom/db/backends/postgresql_psycopg2/base.py", line 73, in ensure_connection
self._reconnect()
File "/usr/lib/python2.7/dist-packages/custom/db/backends/postgresql_psycopg2/base.py", line 63, in _reconnect
self.connect()
File "/usr/local/lib/python2.7/dist-packages/django/db/backends/base/base.py", line 120, in connect
self.set_autocommit(self.settings_dict['AUTOCOMMIT'])
File "/usr/local/lib/python2.7/dist-packages/django/db/backends/base/base.py", line 295, in set_autocommit
self._set_autocommit(autocommit)
File "/usr/local/lib/python2.7/dist-packages/django/db/backends/postgresql_psycopg2/base.py", line 218, in _set_autocommit
self.connection.autocommit = autocommit
File "/usr/local/lib/python2.7/dist-packages/django/db/utils.py", line 97, in __exit__
six.reraise(dj_exc_type, dj_exc_value, traceback)
File "/usr/local/lib/python2.7/dist-packages/django/db/backends/postgresql_psycopg2/base.py", line 218, in _set_autocommit
self.connection.autocommit = autocommit
ProgrammingError: autocommit cannot be used inside a transaction
现在看代码:
from django.db.backends.postgresql_psycopg2.base import DatabaseError, \
IntegrityError, DatabaseWrapper as PostgresWrapper
class DatabaseWrapper(PostgresWrapper):
def _reconnect(self):
try:
self.connect()
except (DatabaseError, OperationalError):
pass
def ensure_connection(self):
"""
Guarantees that a connection to the database is established.
"""
if self.connection is None:
with self.wrap_database_errors:
self._reconnect()
else:
try:
self.connection.cursor().execute('SELECT 1')
except (DatabaseError, OperationalError):
self._reconnect()