GAE数据存储争用问题

时间:2014-09-15 15:38:57

标签: google-app-engine google-cloud-datastore contention entity-groups

我们的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)

2 个答案:

答案 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):

  1. 在app.yaml中延迟启用
  2. 将对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)