我们的GAE应用程序在NDB中制作了另一个网站的关系数据库的本地副本。有4种实体类型 - 用户,表,行,字段。每个用户都有一堆表,每个表都有一堆行,每行有一堆字段。
SomeUser> SomeTable> ARow>更远
因此,每个用户成为一个实体组。我需要一个功能,我可以清除某个用户的所有表(及其行)。删除所有表和所有行的正确方法是什么,同时避免约5次操作/秒的争用限制。
由于实体组的争用,当前代码获得TransactionFailedError
。
(我忽略的细节是我们只想删除属性' service'设置为某个值的表格)
def delete_tables_for_service(user, service):
tables = Tables.query(Tables.service == service, ancestor=user.key).fetch(keys_only=True)
for table in tables:
keys = []
keys += Fields.query(ancestor=table).fetch(keys_only=True)
keys += TableRows.query(ancestor=table).fetch(keys_only=True)
keys.append(table)
ndb.delete_multi(keys)
答案 0 :(得分:1)
如果您要删除的所有实体都在一个实体组中,请尝试在一个事务中删除它们。如果没有显式事务,则每次删除都在其自己的事务中发生,并且所有事务必须排队(通过争用和重试)以更改实体组。
答案 1 :(得分:0)
您确定它是基于争用的,还是因为上面的代码是在事务中执行的?快速修复可能是增加重试次数并为此方法启用跨组事务:
@ndb.transactional(retries=5, xg=True)
您可以在此处详细了解:https://developers.google.com/appengine/docs/python/ndb/transactions。如果这不是罪魁祸首,可以考虑异步延迟或运行删除操作,以便它们随着时间的推移和较小批量执行。 NDB的诀窍是定期进行小规模的工作,而不是很少的工作。以下是将该代码转换为异步工作单元的一种方法:
def delete_tables_for_service(user, service):
tables = Tables.query(Tables.service == service, ancestor=user.key).fetch(keys_only=True)
for table in tables:
# Delete fields
fields_keys = Fields.query(ancestor=table).fetch(keys_only=True)
ndb.delete_multi_async(fields_keys)
# Delete table rows
table_rows_keys = TableRows.query(ancestor=table).fetch(keys_only=True)
ndb.delete_multi_async(table_rows_keys)
# Finally delete table itself
ndb.delete_async(table.key)
如果您想要更好地控制删除,重试,失败,您可以使用任务队列,或者只使用延迟库(https://developers.google.com/appengine/articles/deferred):
将对ndb.delete_multi的调用更改为deferred:
def delete_tables_for_service(user, service):
tables = Tables.query(Tables.service == service, ancestor=user.key).fetch(keys_only=True)
for table in tables:
keys = []
keys += Fields.query(ancestor=table).fetch(keys_only=True)
keys += TableRows.query(ancestor=table).fetch(keys_only=True)
keys.append(table)
deferred.defer(_deferred_delete_tables_for_keys, keys)
def _deferred_delete_tables_for_keys(keys):
ndb.delete_multi(keys)