没有事务时 GAE 上的 TransactionFailedError

TransactionFailedError on GAE when no transaction

我收到这个错误:

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.

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

这让我怀疑在数据存储方面,为了确保跨支持它的冗余基础设施的操作 consistency/reliability,每个写操作实际上都使用 same/similar 机制作为事务操作。不同之处在于,那些在客户端也有一些事务检查,before/after RPC 交换和数据存储的显式 RPC 事务 start/end 触发器。

来自 Life of a Datastore Write 的引述表明,无论操作是否为事务性的,都在使用一些通用机制(强调我的):

If the commit phase has succeeded but the apply phase failed, the datastore will roll forward to apply the changes to indexes under two circumstances:

  1. The next time you execute a read or write or start a transaction on this entity group, the datastore will first roll forward and fully apply this committed but unapplied write, based on the data in the log.

失败的可能原因之一就是对相同实体的并行访问过多,即使它们只是只读的。请参阅 ,尽管在那种情况下它们用于客户端的事务。

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

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

放置 "Every attempt to create, update, or delete an entity takes place in the context of a transaction," 和 "There is a write throughput limit of about one transaction per second within a single entity group," 可能说明您所看到的内容,特别是如果 entity_list 包含属于同一实体组的实体。