在阅读了Google Datastore概念/理论后,我开始使用Go datastore package
方案:
种类User
和LinkedAccount
要求每个用户都有一个或多个链接帐户(yay第三方登录)。为了保持一致性,LinkedAccounts将成为关联用户的子级。然后,新用户创建涉及创建User和LinkedAccount,而不仅仅是一个。
用户创建似乎是交易的完美用例。如果LinkedAccount创建失败,则事务回滚失败。这似乎不可能。目标是在事务中创建父项,然后创建子项。
根据文件
事务中的所有数据存储区操作都必须对其中的实体进行操作 如果事务是单个组事务,则为同一实体组
我们希望新的User
和LinkedAccount
位于同一组中,因此对我来说,听起来像Datastore应该支持这种情况。我担心的是,意图是对同一组中现有实体的操作可以在单个事务中执行。
tx, err := datastore.NewTransaction(ctx)
if err != nil {
return err
}
incompleteUserKey := datastore.NewIncompleteKey(ctx, "User", nil)
pendingKey, err := tx.Put(incompleteUserKey, user)
if err != nil {
return err
}
incompleteLinkedAccountKey := datastore.NewIncompleteKey(ctx, "GithubAccount", incompleteUserKey)
// also tried PendingKey as parent, but its a separate struct type
_, err = tx.Put(incompleteLinkedAccountKey, linkedAccount)
if err != nil {
return err
}
// attempt to commit
if _, err := tx.Commit(); err != nil {
return err
}
return nil
从library source明确为什么这不起作用。 PendingKey
不是密钥,不完整的密钥不能用作父母。
这是数据存储区或库的必要限制吗?对于那些有这种要求的人来说,你是否只是牺牲了强大的一致性并使两种全球化?
对于Google-ability:
答案 0 :(得分:5)
需要注意的一点是Cloud Datastore API can operate on up to 25 entity groups中的事务,但这并不能解决如何在同一个实体组中创建两个实体作为单个事务的一部分的问题。
有几种方法可以解决这个问题(请注意,这适用于任何使用Cloud Datastore API,而不仅仅是gcloud-golang
库):
为父键使用(字符串)名称,而不是让数据存储区自动分配数字ID:
parentKey := datastore.NewKey(ctx, "Parent", "parent-name", 0, nil)
childKey := datastore.NewIncompleteKey(ctx, "Child", parentKey)
明确调用AllocateIds
让数据存储区为父键选择一个数字ID:
incompleteKeys := [1]*datastore.Key{datastore.NewIncompleteKey(ctx, "Parent", nil)}
completeKeys, err := datastore.AllocateIDs(ctx, incompleteKeys)
if err != nil {
// ...
}
parentKey := completeKeys[0]
childKey := datastore.NewIncompleteKey(ctx, "Child", parentKey)