我一直在做一些測試,並已能夠確認使用Django
與PostgreSQL
和PGBouncer
它不會自動重新連接失去它的連接。說實話,我不確定這是否是一個錯誤,或者如果這是設計。如果它是一個錯誤,我會高興地報告它,如果不是,我想解釋爲什麼以及如何繞過它而不是另一個自定義後端。當PostgreSQL死亡時,Django不會重新連接,需要自定義後端?
我很容易做這些測試通過執行以下操作上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
中有代碼運行Select 1
來檢查連接,但似乎沒有工作。回到Django 1.5.x
,我們編寫了一個自定義的PostgreSQL後端來做同樣的事情,但刪除了它,因爲它似乎在我們升級時內置了Django
。這是一個錯誤?
編輯2015年11月6日:在Django中,PostgreSQL後端實現is_usable
功能的數據庫上做了SELECT 1
。但是,除close_if_unusable_or_obsolete
之外,我找不到is_usable
的任何用法,但我找不到任何地方的任何用法。我認爲它會在某些數據庫封裝器中使用,該封裝器基於is_usable
捕獲異常和重試/重新連接。
上面提到的代碼可以在Django 1.8的django/db/backends/postgresql_psychopg2/base.py
找到。
編輯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()