Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/321.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 未回滚所有更改的ndb事务尝试失败?_Python_Google App Engine_Google Cloud Datastore - Fatal编程技术网

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

Python 未回滚所有更改的ndb事务尝试失败?,python,google-app-engine,google-cloud-datastore,Python,Google App Engine,Google Cloud Datastore,我在理解导致我的应用程序中出现错误的一系列事件时遇到了一些问题,这些错误只能在部署在GAE上的应用程序中间歇性地看到,而在使用本地devserver.py运行时则永远看不到 下面所有相关的代码片段(针对MCV进行了修剪,希望我没有丢失任何重要的内容)都是在处理相同的任务队列请求时执行的 切入点: def job_completed_task(self, _): # running outside transaction as query is made if not self.

我在理解导致我的应用程序中出现错误的一系列事件时遇到了一些问题,这些错误只能在部署在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\u 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\u job\u mark\u completed\u transaction()

    task = taskqueue.add(queue_name=self.task_queue_name, url=url, params=params,
                         transactional=ndb.in_transaction(), countdown=delay)
事件的GAE日志因不适合单个屏幕而被拆分:

我对已完成的
作业的期望是查看
。。。作业已完成。。。NOP
调试消息和未排队的任务,或至少查看
状态更改运行->完成
信息消息和
作业排队的任务。触发器\u作业\u标记\u已完成\u事务()

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

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

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

  • 第二次,它找到状态
    done
    ,只打印调试消息


我的问题是-如果第一次事务尝试失败,状态更改是否也应该回滚?我缺少什么?

我找到了一个解决方法:指定对
作业\u completed()不重试。
事务:

@ndb.transactional(xg=True, retries=0)
def jobs_completed(self, job):
这将防止自动重复执行,而是导致异常:

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

这是可以接受的,因为我已经为整个
作业\u完成的\u任务()
建立了回退/重试安全网。现在一切都好了

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