ndb 数据争用越来越严重
ndb data contention getting worse and worse
我有一个奇怪的问题。我在 gae 上有一个模块 运行ning,它将大量小任务放在默认任务队列中。这些任务访问相同的 ndb 模块。每个任务从几个不同的表中访问一堆数据,然后调用 put
.
前几个任务工作正常,但随着时间的推移,我开始在最后完成这些任务 put
:
suspended generator _put_tasklet(context.py:358) raised TransactionFailedError(too much contention on these datastore entities. please try again.)
所以我用一个 try 包装了 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 争用会变得更糟。我同时有很多任务 运行ning,它们以可能导致争用的方式访问 ndb。如果检测到争用,则会发生随机定时重试,这可以很好地消除争用。一小会儿。任务保持 运行 宁和完成,成功的次数越多 return 发生的争用就越多。即使使用竞争数据的过程应该完成。是否发生了不应该持有的数据存储句柄?怎么回事?
编辑 2:
这里有一些关于关键结构的信息:
我的 ndb 模型位于一个层次结构中,我们有这样的东西(箭头的方向指定父子关系,即:类型有一堆子实例等)
Type->Instance->Position
Position的id仅限于几个不同的名称,实例有几千个,类型不多。
我计算了一堆 Position,然后做了一个 try_put_multi(以一种明显的方式类似于 try_put)并获得了竞争。我将很快再次 运行 代码,并在此处包含完整的回溯。
如果您持续超过每秒每个实体组 1 write/transaction,争用会随着时间的推移变得更糟。答案在于 Megastore/Paxo 的工作方式以及 Cloud Datastore 如何处理后端争用。
当在Megastore 的不同节点上同时尝试2 次写入时,一个事务将获胜,另一个将失败。 Cloud Datastore 检测到此争用,并将多次重试失败的事务。通常这会导致事务成功而不会向客户端引发任何错误。
如果尝试持续写入超过建议的限制,则事务需要多次重试的可能性会增加。处于内部重试状态的事务数也会增加。最终,事务将开始达到我们的内部重试限制,并将 return 向客户端发送争用错误。
随机睡眠方法是处理错误响应情况的不正确方法。相反,您应该研究带有抖动的指数退避 (example)。
同样,你的问题的核心是对单个实体组的高写入率。您应该研究是否需要明确的父子关系(如果不需要则将其删除),或者您是否应该根据您的查询和一致性要求以某种有意义的方式对实体组进行分片。
我有一个奇怪的问题。我在 gae 上有一个模块 运行ning,它将大量小任务放在默认任务队列中。这些任务访问相同的 ndb 模块。每个任务从几个不同的表中访问一堆数据,然后调用 put
.
前几个任务工作正常,但随着时间的推移,我开始在最后完成这些任务 put
:
suspended generator _put_tasklet(context.py:358) raised TransactionFailedError(too much contention on these datastore entities. please try again.)
所以我用一个 try 包装了 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 争用会变得更糟。我同时有很多任务 运行ning,它们以可能导致争用的方式访问 ndb。如果检测到争用,则会发生随机定时重试,这可以很好地消除争用。一小会儿。任务保持 运行 宁和完成,成功的次数越多 return 发生的争用就越多。即使使用竞争数据的过程应该完成。是否发生了不应该持有的数据存储句柄?怎么回事?
编辑 2:
这里有一些关于关键结构的信息:
我的 ndb 模型位于一个层次结构中,我们有这样的东西(箭头的方向指定父子关系,即:类型有一堆子实例等)
Type->Instance->Position
Position的id仅限于几个不同的名称,实例有几千个,类型不多。
我计算了一堆 Position,然后做了一个 try_put_multi(以一种明显的方式类似于 try_put)并获得了竞争。我将很快再次 运行 代码,并在此处包含完整的回溯。
如果您持续超过每秒每个实体组 1 write/transaction,争用会随着时间的推移变得更糟。答案在于 Megastore/Paxo 的工作方式以及 Cloud Datastore 如何处理后端争用。
当在Megastore 的不同节点上同时尝试2 次写入时,一个事务将获胜,另一个将失败。 Cloud Datastore 检测到此争用,并将多次重试失败的事务。通常这会导致事务成功而不会向客户端引发任何错误。
如果尝试持续写入超过建议的限制,则事务需要多次重试的可能性会增加。处于内部重试状态的事务数也会增加。最终,事务将开始达到我们的内部重试限制,并将 return 向客户端发送争用错误。
随机睡眠方法是处理错误响应情况的不正确方法。相反,您应该研究带有抖动的指数退避 (example)。
同样,你的问题的核心是对单个实体组的高写入率。您应该研究是否需要明确的父子关系(如果不需要则将其删除),或者您是否应该根据您的查询和一致性要求以某种有意义的方式对实体组进行分片。