0

我们的 GAE 应用程序在 NDB 中制作另一个网站的关系数据库的本地副本。有 4 种实体类型 - 用户、表、行、字段。每个用户都有一堆表,每个表都有一堆行,每一行都有一堆字段。

SomeUser > SomeTable > ARow > AField

因此,每个用户都成为一个实体组。我需要一个可以清除某个用户的所有表(及其行)的功能。什么是删除所有表和所有行的正确方法,同时避免约 5 次操作/秒的争用限制。

TransactionFailedError由于实体组的争用,当前代码正在获取s。(我忽略的细节是我们只想删除属性“服务”设置为某个值的表)

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)
4

2 回答 2

1

如果您要删除的所有实体都在一个实体组中,请尝试在一个事务中将它们全部删除。如果没有显式事务,则每个删除都发生在其自己的事务中,并且所有事务都必须排队(通过争用和重试)以更改实体组。

于 2014-09-15T18:23:12.910 回答
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 ):

  1. 在您的 app.yaml 中打开延迟
  2. 将对 ndb.delete_multi 的调用更改为延迟:

    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)
    
于 2014-09-15T16:30:04.790 回答