django 1.6应用程序如何从数据库中断中恢复



我有一个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

到目前为止,我发现第一个异常是由atomic装饰器中的__exit__引发的。据推测,这是在屏蔽set_unusable_password引发的异常。

close_connection尝试调用abort时,会引发第二个异常。

atomic装饰器中的__enter__甚至在尝试与数据库服务器通信之前就引发了最后一个异常。

除了调用close_connection()来清理旧状态,我还需要做什么,以便建立与数据库的新连接?

事实证明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()中,它显然只是为了支持合法的事务管理。从代码中可以看出,conn.abort的异常可以被安全地忽略。但在我的情况下,conn.abort并没有引发DatabaseError,而是引发了InterfaceError

如果我将close_old_connections的实现复制到我自己的代码中,并对其进行修改以处理DatabaseErrorInterfaceError,我的症状就会消失。

我发现了一个已知的错误,这在某种程度上与我的问题有关。

在研究了close_connectionclose_old_connections内部的实际情况后,我得出了以下几行,据我所知,这些行做了正确的事情:

try: django.db.connection.abort()
except django.db.utils.Error: pass
django.db.connection.close()

最新更新