如何使用Google云数据存储nodejs读取交易中的所有实体

时间:2018-02-09 14:28:15

标签: node.js google-app-engine transactions google-cloud-datastore

当我尝试运行查询以使用google数据存储区读取交易中的所有实体时,它会给我这个错误

{ Error: Only ancestor queries are allowed inside transactions.
    at /root/src/node_modules/grpc/src/client.js:554:15
  code: 3,
  metadata: Metadata { _internal_repr: {} },

所以我需要使用祖先查询。如何创建祖先查询?它似乎取决于您在数据存储区中构建层次结构的方式。所以我的下一个问题是,鉴于我在数据存储区中创建的每个实体都已经这样保存(标识符对于保存的entityData是唯一的)

const entityKey = datastore.key({ namespace: ns, path: [kind, identifier] });
{ key: entityKey, method: 'upsert', data: entityData };

如何在事务中读取数据库?如果我知道标识符,我想我可以做到,但标识符是从我保存的类型中的entityData构造的,我需要读取类型的实体来弄清楚我在db中有什么(鸡蛋问题) 。我希望我错过了什么。

更多背景

我的问题涉及赞助人。我在数据存储区中存储了一种people,其中每个实体都是person,由唯一标识符,名称和等级组成。我还有另一种名为relationships的实体,其中每个实体都是relationship,其中包含两个人名标识符sponsor& sponsee(与人们联系在一起)。所以我把它的结构就像一个RDB。如果我想获得一个人赞助商,我从db获取所有关系,循环它们返回personsponsee的关系,然后查询数据库中的sponsor relationship

如何构建数据存储区'方式,实体组/祖先,因为我必须模拟人和他们的链接/关系。

让我们假设一个RDB是不可能的。

示例方案

必须从app / db中删除两个人(让他们说他们在同一天离开了公司)。当我删除某人时,我也想删除他们的关系。我删除的两个人分享一个关系(一个赞助另一个)。假设第一个交易成功,即我删除一个人及其关系。下一个事务,我删除一个人,然后搜索关系的相关关系,我找到一个已被删除,因为最终一致。我试着找到那个关系的人而且他们不存在。爆炸。

注意:每个事务都包含删除人员和&他们的关系。多人等于多笔交易。

我的应用程序不关心可扩展性

1 个答案:

答案 0 :(得分:2)

您的理解是正确的:

  • 您不能使用祖先查询,因为您的实体不属于祖先关系(即不在同一实体组中)。

  • 您无法在事务内执行非祖先查询。请注意,您还无法在单个事务中读取超过25个实体(每个实体位于单独的实体组中)。来自Restrictions on queries

  

事务内部的查询必须是祖先查询

     

云数据存储transactions对属于up的实体进行操作   到25 entity groups,但在事务内的查询必须是   祖先查询。在事务中执行的所有查询都必须   specify an ancestor。有关更多信息,请参阅Datastore Transactions

与您类似的上下文中的典型方法是在事务外执行查询(通常只是keys only查询 - 获取实体键,然后通过键查找读取相应的实体(一次最多25个)内部交易。并且仅在绝对需要时才使用交易,例如,请参阅此相关讨论:Ancestor relation in datastore

您的问题显然表明您正在使用关系数据库思维模式来接近数据存储区。如果您的应用程序从根本上需要关系数据(您没有描述您要执行的操作),则数据存储可能不是最适合它的产品。见Choosing a storage option。我不是说您不能将数据存储区与关系数据一起使用,它仍然可以在很多情况下完成,但是设计得更加谨慎 - 这些限制正在推动可扩展的基于数据存储区的应用程序(恕我直言,可能更具可扩展性)你可以通过关系数据库来实现)

构建数据RDB样式(对数据存储区是正常的)和以RDB样式使用它(这不是那么好)之间存在差异。

在您提到的特定使用方案中,您无需查询sponsor的{​​{1}}:relationship中已有sponsor个密钥实体,您需要做的就是按键查找,这可以在事务中完成。

获取relationship的所有relationship个实体需要一个查询,并由person personsponsor进行过滤。但它真的必须在交易中完成吗?或者,如果您错过了几秒前创建的sponsee结果列表,是否可以接受?或者最近删除了一个?如果稍后重复查询,它最终会(不显示)出现在列表中(参见Eventual Consistency on Reading an Index)。如果这是可以接受的(恕我直言,关系不会经常改变,在更改后完全查询的机会相当渺茫)那么你不需要在事务中进行查询,因此你不需要祖先关系在relationshippeople个实体之间。非常适合扩展性。

另一个考虑因素:循环遍历relationship实体列表:也不一定要在事务中完成。并且,如果关系数量很大,则循环可以达到请求截止日期。一种更具可伸缩性的方法是使用查询游标并将工作分成多个任务/请求,每个任务/请求处理一个列表的子集。请参阅此类方法的Python示例:How to delete all the entries from google datastore?

对于每个relationship删除案例:

  • person添加being_deleted属性(在事务中)以标记删除并阻止删除期间的任何使用,例如在删除任务正在进行时创建新关系。在应用程序的逻辑中(也在事务中)在需要的地方添加对此标志的检查。
  • 使用上面提到的循环技术获取该人的所有person密钥列表并删除它们
  • 在最后一个循环迭代中,当没有关系时,将另一个任务排队,慷慨地延迟,重新检查由于最终​​一致性而在前一个循环执行中可能遗漏的任何最近关系。如果有任何显示重新运行循环,否则只需删除relationship

如果不考虑可扩展性,您还可以重新设计数据结构,以便在所有实体之间使用祖先(将它们放在同一个实体组中),然后就可以按照自己的意愿行事。例如,请参阅What would be the purpose of putting all datastore entities in a single group?。但是要注意许多潜在的风险,例如: