我有点奇怪的问题。我有一个在gae上运行的模块,它将大量小任务放在默认任务队列中。这些任务访问同一个ndb模块。每个任务从几个不同的表中访问一堆数据,然后调用put
。
前几个任务工作正常,但随着时间的推移,我开始在最终的put
上获得这些任务:
suspended generator _put_tasklet(context.py:358) raised TransactionFailedError(too much contention on these datastore entities. please try again.)
所以我试了一下put并放入一个随机超时,所以重试了几次。这稍微缓解了这个问题,稍后就会发生。
以下是我的任务的伪代码:
def my_task(request):
stuff = get_ndb_instances() #this accessed a few things from different tables
better_stuff = process(ndb_instances) #pretty much just a summation
try_put(better_stuff)
return {'status':'Groovy'}
def try_put(oInstance,iCountdown=10):
if iCountdown<1:
return oInstance.put()
try:
return oInstance.put()
except:
import time
import random
logger.info("sleeping")
time.sleep(random.random()*20)
return oInstance.try_put(iCountdown-1)
在不使用try_put
的情况下,队列将获得大约30%的时间,直到它停止工作。随着try_put的进一步发展,例如60%。
在某个任务以某种方式完成后,任务是否正在保留ndb连接?我没有明确使用交易。
编辑:
对于我所要求的内容似乎有些混乱。问题是:为什么随着时间的推移,ndb争用会变得更糟。我有很多任务同时运行,他们以一种可能导致争用的方式访问ndb。如果检测到争用,则发生随机定时重试,这完全消除了争用。一小会儿。任务保持运行和完成,成功返回的争用越多,争用就越多。即使使用争用数据的进程应该完成。是否有一些东西正在持有不应该的数据存储区句柄?发生了什么事?EDIT2:
这里有一些关于游戏中的关键结构:
我的ndb模型位于层次结构中,我们有这样的东西(箭头的方向指定父子关系,即:Type有一堆子实例等)
Type->Instance->Position
职位的ID仅限于几个不同的名称,有数千个实例,而且类型不多。
我计算了一堆位置,然后执行try_put_multi(以明显的方式类似于try_put)并获得争用。我将很快再次运行代码并获得完整的回溯以包含在这里。
答案 0 :(得分:0)
如果每秒不超过每个实体组的1次写入/事务,则争用将会加剧。答案是Megastore / Paxo如何工作以及Cloud Datastore如何处理后端的争用。
当在Megastore中的不同节点上同时尝试2次写入时,一个事务将获胜而另一个事务将失败。 Cloud Datastore会检测到此争用,并会多次重试失败的事务。通常这会导致事务成功,而不会将任何错误提交给客户端。
如果尝试持续写入高于建议限制,则需要多次重试事务的可能性会增加。内部重试状态中的事务数也会增加。最终,事务将开始达到我们的内部重试限制,并将向客户端返回争用错误。
随机睡眠方法是处理错误响应情况的错误方法。您应该使用抖动(example)来查看指数后退。
同样,问题的核心是对单个实体组的高写入率。您应该查看是否需要显式父级(如果不需要,则删除它),或者是否应该根据您的查询和一致性要求以某种方式对实体组进行分片。