我是 GAE 新手,对使用 DataStore 进行事务有一些疑问。
例如,我有一个用户实体,该实体是当用户在 Facebook 上添加我的应用程序时创建的。我通过 Facebook API 获得了一些属性,但我想为用户添加用户名,并且该用户名必须是唯一的。所以在事务范围内我调用这个方法:
def ExistsUsernameToDiferentUser(self, user, username):
query = User.all()
query.filter("username", username)
query.filter("idFacebook != ", user.idFacebook)
userReturned = query.get()
return True if userReturned else False
但是 GAE 给了我这个错误:
BadRequestError: queries inside transactions must have ancestors
好的,我明白了,但是用户没有任何祖先,它是根实体。我必须做什么?
最佳答案
我明白你现在想做什么。
通过强制使用祖先,数据存储会强制您锁定数据存储的一部分(给定祖先下的所有内容),以便您可以保证该部分的一致性。然而,要做你想做的事,你本质上需要锁定所有 User 实体来查询某个 User 实体是否存在,然后创建一个新的 User 实体,然后解锁它们。
你可以这样做,只需创建一个实体,它可以是一个空实体,但确保它有一个唯一的键(如“用户祖先”),保存它,并使其成为每个用户实体的祖先。 这可能是一个坏主意,因为这会限制您在用户实体上的性能,特别是在写入方面。每次创建新用户时,所有用户实体都将被阻止更新。
我试图说明在 HRD 世界中您需要如何以不同的方式思考交易。您可以自行构建数据(使用祖先),以便为您的特定应用程序获得良好的性能特征。事实上,您可能不同意我的观点,并说用户实体更新的频率非常低,因此可以将它们全部锁定。
出于说明目的,另一种短视的可能性是根据用户名创建多个祖先。即,字母表中的每个字母对应一个。然后当你需要创建一个新的User时,你可以根据相应的祖先进行搜索。虽然这比拥有单一祖先有所改进(好 26 倍),但它仍然会预先限制您 future 的表现。如果您现在知道最终将拥有的用户总数,这可能没问题,但我怀疑您需要数亿用户。
最好的方法是返回其他建议并将用户名作为 key 。这为您提供了最佳的可扩展性,因为通过按键获取/设置用户实体可以是事务性的,并且不会锁定其他实体,从而限制您的可扩展性。
您需要找到一种方法来解决这个问题。例如,您在用户名之前获得的任何信息都可以存储在另一个实体中,该实体具有与稍后创建的用户相关的字段。或者,您可以在通过 key 创建用户实体后将该数据复制到用户实体中,然后删除原始实体。
关于google-app-engine - 根实体中的 GAE 事务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13662348/