失败的ndb事务尝试没有回滚所有更改?

时间:2016-10-07 14:40:01

标签: python google-app-engine google-cloud-datastore

我在理解导致我的appplication中的错误的一系列事件时遇到了一些麻烦,这些错误只能在GAE上部署的应用中看到间歇性,而在使用本地devserver.py运行时永远不会

在处理相同的任务队列请求期间,执行下面的所有相关代码片段(为MCV修剪,希望我没有丢失任何重要信息)。

切入点:

def job_completed_task(self, _):

    # running outside transaction as query is made
    if not self.all_context_jobs_completed(self.context.db_key, self):
        # this will transactionally enqueue another task
        self.trigger_job_mark_completed_transaction()
    else:
        # this is transactional
        self.context.jobs_completed(self)

相应的self.context.jobs_completed(self)是:

@ndb.transactional(xg=True)
def jobs_completed(self, job):

    if self.status == QAStrings.status_done:
        logging.debug('%s jobs_completed %s NOP' % (self.lid, job.job_id))
        return

    # some logic computing step_completed here

    if step_completed:
        self.status = QAStrings.status_done  # includes self.db_data.put()

    # this will transactionally enqueue another task
    job.trigger_job_mark_completed_transaction()

self.status setter,被黑客攻击以获取用于调试此场景的回溯:

@status.setter
def status(self, new_status):
    assert ndb.in_transaction()

    status = getattr(self, self.attr_status)
    if status != new_status:
        traceback.print_stack()
        logging.info('%s status change %s -> %s' % (self.name, status, new_status))
        setattr(self, self.attr_status, new_status)

job.trigger_job_mark_completed_transaction()最终将这样的新任务排入队列:

    task = taskqueue.add(queue_name=self.task_queue_name, url=url, params=params,
                         transactional=ndb.in_transaction(), countdown=delay)

出现的GAE日志,因为它不适合单个屏幕而分开:

enter image description here

enter image description here

我对jobs_completed事务的期望是要么看到... jobs_completed ... NOP调试消息而没有排队的任务,要么至少看到status change running -> done信息消息和{{1}排队的任务}}

我实际看到的是消息和没有任务排队。

日志显示表明交易尝试了两次:

  • 第一次发现状态不是job.trigger_job_mark_completed_transaction(),所以它执行逻辑,将状态设置为done(并显示回溯和信息消息),并且应该在事务上将新状态排入队列任务 - 但它没有

  • 第二次找到状态done并打印调试消息

我的问题是 - 如果第一次交易尝试失败,状态更改是否也不会回滚?我错过了什么?

1 个答案:

答案 0 :(得分:0)

我找到了一种解决方法:不指定jobs_completed()事务的重试次数:

@ndb.transactional(xg=True, retries=0)
def jobs_completed(self, job):

这可以防止自动重复执行,而不是导致异常:

  

TransactionFailedError(无法提交交易。请   再试一次。)

这是可以接受的,因为我已经为整个job_completed_task()设置了退避/重试安全网。现在一切都好。

至于为什么回滚没有发生,我唯一想到的就是在进入交易之前不知何故实体被读取(并在我的对象属性中缓存),因此不被认为是(同)交易。但我无法找到能够做到这一点的代码路径,所以这只是猜测。