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

时间:2014-09-08 12:34:38

标签: django psycopg2

我有一个带有长时间运行守护进程的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()以清理旧状态之外,我还需要做些什么,以便我可以建立与数据库的新连接?

1 个答案:

答案 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的实现复制到我自己的代码中并修改它以处理DatabaseErrorInterfaceError,我的症状就会消失。

我发现了一个known bug,这与我的问题有些相关。

在查看close_connectionclose_old_connections内部实际发生的事情之后,我想出了以下几行,据我所知,这是正确的:

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