我们的GAE应用程序在NDB中制作另一个网站关系数据库的本地副本。有4种实体类型-用户、表、行、字段。每个用户有一堆表,每个表有一堆行,每行有一堆字段。
SomeUser>SomeTable>ARow>A字段
因此,每个用户成为一个实体组。我需要一个可以为某个用户清除所有表(及其行)的功能。删除所有表和所有行的正确方法是什么,同时避免每秒5次操作的争用限制。
由于实体组上存在争用,当前代码正在获取TransactionFailedError
s。(我忽略的细节是,我们只想删除属性"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)
如果要删除的所有实体都在一个实体组中,请尝试在一个事务中全部删除它们。如果没有显式事务,每次删除都发生在自己的事务中,所有事务都必须排队(通过争用和重试)才能更改实体组。
您确定它是基于争用的,还是因为上面的代码是在事务中执行的?快速解决方法可能是增加重试次数,并为此方法启用跨组事务:
@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):
- 在你的app.yaml中打开延迟
将对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)