没有事务时GAE上的TransactionFailedError

时间:2017-09-22 14:50:07

标签: google-app-engine app-engine-ndb google-app-engine-python

我收到了这个错误:

TransactionFailedError: too much contention on these datastore entities. please try again.

即使我没有做任何交易。导致错误的代码行是

ndb.put_multi(entity_list) # entity_list is a list of 100 entities

这个错误并不经常发生,所以这不是什么大问题,但我很好奇为什么我会收到这个错误。有什么想法吗?

以下是大部分追溯:

Traceback (most recent call last):
  ...
  File "/base/data/home/runtimes/python27_experiment/python27_lib/versions/1/google/appengine/ext/deferred/deferred.py", line 318, in post
    self.run_from_request()
  File "/base/data/home/runtimes/python27_experiment/python27_lib/versions/1/google/appengine/ext/deferred/deferred.py", line 313, in run_from_request
    run(self.request.body)
  File "/base/data/home/runtimes/python27_experiment/python27_lib/versions/1/google/appengine/ext/deferred/deferred.py", line 155, in run
    return func(*args, **kwds)
  File "/base/data/home/apps/s~opavote/2017-09-15.404125237783169549/tasks.py", line 70, in start_election
    models.Voter.create(e.eid, chunk)
  File "/base/data/home/apps/s~opavote/2017-09-15.404125237783169549/models.py", line 2426, in create
    ndb.put_multi(voters + vbs)
  File "/base/data/home/runtimes/python27_experiment/python27_lib/versions/1/google/appengine/ext/ndb/model.py", line 3958, in put_multi
    for future in put_multi_async(entities, **ctx_options)]
  File "/base/data/home/runtimes/python27_experiment/python27_lib/versions/1/google/appengine/ext/ndb/tasklets.py", line 383, in get_result
    self.check_success()
  File "/base/data/home/runtimes/python27_experiment/python27_lib/versions/1/google/appengine/ext/ndb/tasklets.py", line 427, in _help_tasklet_along
    value = gen.throw(exc.__class__, exc, tb)
  File "/base/data/home/runtimes/python27_experiment/python27_lib/versions/1/google/appengine/ext/ndb/context.py", line 824, in put
    key = yield self._put_batcher.add(entity, options)
  File "/base/data/home/runtimes/python27_experiment/python27_lib/versions/1/google/appengine/ext/ndb/tasklets.py", line 427, in _help_tasklet_along
    value = gen.throw(exc.__class__, exc, tb)
  File "/base/data/home/runtimes/python27_experiment/python27_lib/versions/1/google/appengine/ext/ndb/context.py", line 358, in _put_tasklet
    keys = yield self._conn.async_put(options, datastore_entities)
  File "/base/data/home/runtimes/python27_experiment/python27_lib/versions/1/google/appengine/ext/ndb/tasklets.py", line 513, in _on_rpc_completion
    result = rpc.get_result()
  File "/base/data/home/runtimes/python27_experiment/python27_lib/versions/1/google/appengine/datastore/datastore_rpc.py", line 928, in get_result
    result = rpc.get_result()
  File "/base/data/home/runtimes/python27_experiment/python27_lib/versions/1/google/appengine/api/apiproxy_stub_map.py", line 613, in get_result
    return self.__get_result_hook(self)
  File "/base/data/home/runtimes/python27_experiment/python27_lib/versions/1/google/appengine/datastore/datastore_rpc.py", line 1893, in __put_hook
    self.check_rpc_success(rpc)
  File "/base/data/home/runtimes/python27_experiment/python27_lib/versions/1/google/appengine/datastore/datastore_rpc.py", line 1385, in check_rpc_success
    raise _ToDatastoreError(err)
TransactionFailedError: too much contention on these datastore entities. please try again.

2 个答案:

答案 0 :(得分:3)

请注意,错误实际上是从RPC响应中的数据存储区本身收到的:self.check_rpc_success(rpc)

这使我怀疑在数据存储方面,为了确保支持它的冗余部分的操作一致性/可靠性,每个写操作实际上使用与事务操作相同/类似的机制。不同之处在于,在RPC交换之前/之后,客户端也有一些事务检查,并且可能是数据存储的显式RPC事务开始/结束触发器。

来自Life of a Datastore Write,引用了一些引用一些共同机制的报价,无论交易是否属于交易行为(强调我的):

  

如果提交阶段成功但应用阶段失败,则   数据存储区将前滚以将更改应用于两个以下的索引   情况:

     
      
  1. 下次在此实体组上执行读取或写入或启动事务时,数据存储区将首先滚动   转发并完全应用此承诺但未应用的写入,基于   日志中的数据。
  2.   

失败的一个可能原因是对同一实体的并行访问过多,即使它们只是只读。请参阅Contention problems in Google App Engine,但在这种情况下,他们会在客户端进行交易。

请注意,这只是一个理论;)

答案 1 :(得分:2)

值得重新审核transactions and entity groups,注意各种定义和限制。

提出“每次创建,更新或删除实体的尝试都发生在事务的上下文中”,并且“在单个实体组中,每秒大约有一个事务的写入吞吐量限制,”可能会说你所看到的,特别是如果entity_list包含属于同一实体组的实体。