【问题标题】:Failed ndb transaction attempt not rolling back all changes?失败的 ndb 事务尝试不回滚所有更改?
【发布时间】:2017-02-16 14:40:41
【问题描述】:

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

下面的所有相关代码 sn-ps(为 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 日志,因为它不适合单个屏幕而被拆分:

我对@9​​87654333@ 事务的期望是要么看到... jobs_completed ... NOP 调试消息并且没有任务入队,或者至少看到status change running -> done 信息消息和job.trigger_job_mark_completed_transaction() 入队的任务。

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

日志似乎表明该事务被尝试了两次:

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

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

我的问题是 - 如果第一次事务尝试失败,不应该回滚状态更改吗?我错过了什么?

【问题讨论】:

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


    【解决方案1】:

    我找到了解决方法:指定不重试jobs_completed() 事务:

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

    这样可以防止自动重复执行,而是引发异常:

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

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

    至于为什么没有发生回滚,我唯一想到的是在进入事务之前以某种方式读取了实体(并缓存在我的对象属性中),因此不被视为(相同) 交易。但是我找不到可以做到这一点的代码路径,所以这只是猜测。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-05-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多