1

我一直在做一些测试,并且能够确认使用DjangowithPostgreSQL并且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()
4

1 回答 1

3

我想我明白了……终于。不完全确定我的第一种方法出了什么问题,但这种方法似乎工作得更好。

class DatabaseWrapper(PostgresWrapper):
    def _cursor(self):
        if self.connection is not None:
            if not self.is_usable():
                self.connection.close()
                self.connection = None
        return super(DatabaseWrapper, self)._cursor()

编辑:最终开源了这个。我不确定它是否 100% 需要,但在服务器上重新启动 Postgres 服务后它可以正常工作。你可以在 pypi asdjango-postgreconnect和 GitHub 上找到它:https ://github.com/mackeyja92/django-postgreconnect 。

于 2015-11-06T23:23:34.327 回答