我有一个带有长时间运行守护进程的django 1.6应用程序,它需要优雅地处理数据库中断。我正在使用django.db.backends.postgresql_psycopg2
引擎。
到目前为止我尝试过的是在处理完DatabaseError并记录之后,我尝试关闭数据库连接,等待一段时间让下一个事务创建一个新连接。
这对我来说效果不佳。以下代码演示了此问题:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from core.models import User
from django.db import close_connection
from django.db.transaction import atomic
from time import sleep
@atomic(savepoint=False)
def do_something():
u = User.objects.get(username='brian')
sleep(3)
u.set_unusable_password()
u.save()
try: do_something()
except Exception as e: print 'A:', e
try: close_connection()
except Exception as e: print 'B:', e
sleep(3)
try: do_something()
except Exception as e: print 'C:', e
如果数据库连接在第一个事务中的睡眠调用期间关闭,并在两个事务之间的睡眠调用期间恢复,我会看到:
A: connection already closed
B: connection already closed
C: The outermost 'atomic' block cannot use savepoint = False when autocommit is off.
如果我改用savepoint=True
,输出看起来会略有不同:
A: connection already closed
B: connection already closed
C: connection already closed
到目前为止,我发现__exit__
装饰器中atomic
引发了第一个异常。据推测,这掩盖了set_unusable_password
引发的异常。
close_connection
尝试调用abort
时会引发第二个例外。
__enter__
装饰器中的atomic
在尝试与数据库服务器通信之前引发了最终异常。
除了调用close_connection()
以清理旧状态之外,我还需要做些什么,以便我可以建立与数据库的新连接?
答案 0 :(得分:0)
事实证明close_connection
已被弃用。我在源头找到了这个:
def close_connection(**kwargs):
warnings.warn(
"close_connection is superseded by close_old_connections.",
PendingDeprecationWarning, stacklevel=2)
使用close_old_connections
代替close_connection
并不会改变任何内容。问题表现完全一样。 close_old_connections
的来源确实看起来很有希望:
def close_old_connections(**kwargs):
for conn in connections.all():
# Remove this when the legacy transaction management goes away.
try:
conn.abort()
except DatabaseError:
pass
conn.close_if_unusable_or_obsolete()
结果显示conn.abort()
中出现的问题,显然只是支持legacty事务管理。从代码中可以看出,conn.abort
的异常可以被安全地忽略。但就我而言,conn.abort
并未提出DatabaseError
而是提升InterfaceError
。
如果我将close_old_connections
的实现复制到我自己的代码中并修改它以处理DatabaseError
和InterfaceError
,我的症状就会消失。
我发现了一个known bug,这与我的问题有些相关。
在查看close_connection
和close_old_connections
内部实际发生的事情之后,我想出了以下几行,据我所知,这是正确的:
try: django.db.connection.abort()
except django.db.utils.Error: pass
django.db.connection.close()