Google app engine 没有事务时GAE上的TransactionFailedError
我得到了这个错误:Google app engine 没有事务时GAE上的TransactionFailedError,google-app-engine,app-engine-ndb,google-app-engine-python,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 这个错误并不经常发生,所以也没什么大不了的,但我很好奇为什么会出现这个错误。有什么想法吗 以下是大部分回溯: Trac
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\u RPC\u success(RPC)
这让我怀疑,在数据存储端,为了确保支持它的infra的冗余部分之间的操作一致性/可靠性,每个写操作实际上都使用与事务操作相同/相似的机制。区别在于,在RPC交换之前/之后,在客户端也有一些事务检查,可能还有数据存储的显式RPC事务开始/结束触发器
从中,引用了一段话,表明无论操作是否为事务性操作,都在使用一些通用机制(重点是我的):
如果提交阶段已成功,但应用阶段失败,则
数据存储将前滚以将更改应用于两个索引下的索引
情况:
请注意,这只是一个理论;) 请注意,错误实际上是从RPC响应中的数据存储本身接收的:
self.check\u RPC\u success(RPC)
这让我怀疑,在数据存储端,为了确保支持它的infra的冗余部分之间的操作一致性/可靠性,每个写操作实际上都使用与事务操作相同/相似的机制。区别在于,在RPC交换之前/之后,在客户端也有一些事务检查,可能还有数据存储的显式RPC事务开始/结束触发器
从中,引用了一段话,表明无论操作是否为事务性操作,都在使用一些通用机制(重点是我的):
如果提交阶段已成功,但应用阶段失败,则
数据存储将前滚以将更改应用于两个索引下的索引
情况:
请注意,这只是一个理论;) 可能需要重新审查,注意各种定义和限制
将“创建、更新或删除实体的每次尝试都发生在事务上下文中”,以及“在单个实体组中每秒有一个事务的写入吞吐量限制”,可能说明了您所看到的情况,特别是如果
实体\u列表
包含可能属于同一实体组的实体。可能需要重新审查,注意各种定义和限制
将“创建、更新或删除实体的每次尝试都发生在事务上下文中”,以及“在单个实体组中每秒有一个事务的写入吞吐量限制”,可能说明了您所看到的情况,特别是如果
实体\u列表
包含属于同一实体组的实体。此处放置的实体都是创建的,我没有使用任何实体组。听起来像是在幕后使用事务来创建实体。我觉得奇怪的是,在创建实体时可能会发生争用。您是提供自己的密钥名,还是让ID自动分配?如果密钥非常接近(例如,顺序),则前者可能导致争用问题。自动分配的ID避免了这一点。我确实有自定义的密钥名,它们是通过将不同实体的ID与用户的电子邮件地址连接起来创建的,比如12345678_joe@example.com. 在某些情况下,电子邮件地址将全部来自同一个域,因此密钥的唯一区别是“u”和“@”。这会使密钥足够紧密,从而可能导致争用问题吗?如果entity\u组中的所有(或许多)实体共享该密钥,我的最佳选择是这足以导致争用。@JeffO'Neill,如果前缀ID(在“\u”之前)如果按顺序递增,则前缀ID非常接近的实体也可能存储在同一个“平板电脑”中,这可能会创建您体验过的热点。最终,在自我优化的努力下,数据存储将拆分平板电脑中的实体序列,并在以后将部分移动到不同的平板电脑中。但最好的方法是让数据存储阻止热点并使用自动分配的ID(即使在为键添加字符串时)。顺便说一句,相似的热点可能出现在非常接近的索引时间戳上。这里放置的实体都是创建的,我没有使用任何实体组。听起来像是在幕后使用事务来创建实体。我觉得奇怪的是,在创建实体时可能会发生争用。您是提供自己的密钥名,还是让ID自动分配?前者可以引起争论