使用sqlalchemy + postgresql

时间:2016-08-31 15:50:55

标签: postgresql sqlalchemy celery pyramid

这是我的代码看起来像

import transaction

@app.task(name='task_name')
def task_name_fn(*args, **kwargs):
   with transaction.manager:
       try:
          actual_fn(*args, **kwargs)
          transaction.commit()
       except:
          transaction.abort()

然而,我的transaction.abort()似乎没有回滚。此工作人员的所有后续芹菜任务都失败了。我收到以下错误

  

由于在刷新期间发生了先前的异常,此会话的事务已被回滚。要使用此Session开始新事务,请首先发出Session.rollback()。

我做错了什么? 更好的问题是,您如何编写task_name_fn以便不会发生此问题?

1 个答案:

答案 0 :(得分:5)

首先,您不需要捕获异常来中止事务。

import transaction

@app.task(name='task_name')
def task_name_fn(*args, **kwargs):
    with transaction.manager:
        actual_fn(*args, **kwargs)

如果发生异常,交易将被中止。

接下来,你可以在任务装饰器中抽象出来。像这样的事情(未经过测试,但可能按原样工作):

from functools import wraps
import transaction

def tm_task(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        with transaction.manager:
            return f(*args, **kwargs)
    return app.task()(decorated)

@tm_task
def actual_fn(*args, **kwargs):
    pass # your function code here instead of calling other function

此外,由于您正在使用事务,因此您可能希望在事务提交后延迟作业的排队。因为,例如,如果在事务中插入一行并将作业排队以对该行执行某些操作,则它可能会在提交第一个事务之前到达该工作线程,并且该行在该事务之外尚不可用。类似的东西:

class AfterCommitTask(Task):
    def apply_async(self, *args, **kw):
        tx = transaction.get()
        def hook(status):
            if status: # Only queue if the transaction was succesfull.
                super(AfterCommitTask, self).apply_async(*args, **kw)
        tx.addAfterCommitHook(hook)

def tm_task(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        with transaction.manager:
            return f(*args, **kwargs)
    return app.task(base=AfterCommitTask)(decorated)

@tm_task
def actual_fn(*args, **kwargs):
    pass # your function code here instead of calling other function