GAE中根实体的查询非常一致?

时间:2014-04-22 05:39:41

标签: python google-app-engine google-cloud-datastore

我想就在Google App Engine中进行强大一致性读/写的最佳方式提出一些建议。

我的数据存储在这样的类中。

class UserGroupData(ndb.Model):
  users_in_group = ndb.StringProperty(repeated=True)
  data = ndb.StringProperty(repeated=True)

我想为这些数据编写一个安全的更新方法。据我所知,我需要避免最终一致的读取,因为它们会冒数据丢失的风险。例如,以下代码是不安全的,因为它使用最终一致的vanilla查询:

def update_data(user_id, additional_data):
  entity = UserGroupData.query(UserGroupData.users_in_group==user_id).get()
  entity.data.append(additional_data)
  entity.put()

如果查询返回的实体是陈旧的,则数据会丢失。

为了实现强大的一致性,似乎我有几个不同的选择。我想知道哪个选项最好:

选项1:

使用始终非常一致的get_by_id()。但是,在这里做这件事似乎并不是一种巧妙的方式。没有直接从UserGroupData派生user_id密钥的简洁方法,因为这种关系是多对一的。要求我的外部客户端存储和发送UserGroupData的密钥似乎有点脆弱和危险。

选项2: 将我的实体放在祖先组中,并执行祖先查询。类似的东西:

def update_data(user_id, additional_data):
  entity = UserGroupData.query(UserGroupData.users_in_group==user_id,
                               ancestor=ancestor_for_all_ugd_entities()).get()
  entity.data.append(additional_data)
  entity.put()

我认为这应该有效,但将所有UserGroupData个实体放入一个祖先组似乎是一件极端的事情。它导致写入限制在~1 /秒。这似乎是错误的方法,因为每个UserGroupData实际上在逻辑上是独立的。 我真正想要做的是对根实体执行强一致的查询。有办法做到这一点吗?我注意到一个建议in another answer基本上会对祖先群体进行分片。这是最好的吗?

选项3:

第三个选项是执行keys_only后跟get_by_id()的查询,如下所示:

def update_data(user_id, additional_data):
  entity_key = UserGroupData.query(UserGroupData.users_in_group==user_id,
                                   ).get(keys_only=True)
  entity = entity_key.get()
  entity.data.append(additional_data)
  entity.put()

据我所知,这种方法可以避免数据丢失,因为我的密钥没有变化,get()给出了非常一致的结果。但是,我还没有看到过这种方法。这是合理的事吗?它有什么缺点我需要理解吗?

1 个答案:

答案 0 :(得分:3)

我认为您还将不一致查询的问题与数据的安全更新混为一谈。

如果user_id在组中,那么像示例UserGroupData.query(UserGroupData.users_in_group==user_id).get()中的查询将始终只返回一个实体。

如果刚刚添加了索引并且索引不是最新的,那么您将无法获得记录,因此您不会更新记录。

任何更新,无论获取实体的方法如何,都应在事务内部执行,以确保更新的一致性。

对于提高查询一致性的祖先,如果您计划拥有多个UserGroupData实体,那么这一点并不明显。在哪种情况下你为什么要做一个get()。

所以选项3,可能是你最好的选择,只做密钥查询,然后在一个事务内做Key.get()并更新。请记住,跨群组交易仅限于5个实体组。

如果查询所基于的索引已经过时,则采用这种方法,则可能会发生以下三种情况中的一种,

  1. 找不到您想要的记录,因为新添加的用户ID未反映在索引中。
  2. 找到您想要的记录,get()将一致地获取它
  3. 找到了您想要的记录,但实际上已删除了用户ID并且索引已过期。 get()将一致地检索索引,并且不存在用户标识。
  4. 然后,您的代码可以决定采取何种行动。

    查询特定用户所需的所有UserGroupData实体需要更新的用例是什么?

相关问题