数据存储:在实体组事务中创建父实体和子实体?
Datastore: Create parent and child entity in an entity group transaction?
阅读 Google 数据存储后 concepts/theory 我开始使用 Go datastore package
场景:
User
和 LinkedAccount
类型要求每个用户都有一个或多个链接帐户(是的第 3 方登录)。为了高度一致,LinkedAccounts 将是关联用户的子级。然后创建新用户涉及创建一个用户和一个 LinkedAccount,而不仅仅是一个。
用户创建似乎是交易的完美用例。如果说 LinkedAccount 创建失败,则事务回滚失败。这目前看来是不可能的。目标是在事务中创建一个父项,然后创建一个子项。
根据文档
All Datastore operations in a transaction must operate on entities in
the same entity group if the transaction is a single group transaction
我们希望新的 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
不是密钥,不完整的密钥不能用作父项。
这是 Datastore 或库的必要限制吗?对于那些有过这种需求的人,你们是不是只是牺牲了强一致性而将这两种需求都变成了全局?
对于Google能力:
- 数据存储:无效密钥
- 数据存储:不能使用 pendingKey 作为类型 *"google.golang.org/cloud/datastore".Key
需要注意的一件事是 Cloud Datastore 中的事务 API can operate on up to 25 entity groups,但这并没有回答如何在同一实体组中创建两个实体作为单笔交易。
有几种方法可以解决这个问题(请注意,这适用于云数据存储 API 的任何使用,而不仅仅是 gcloud-golang
库):
为父键使用(字符串)名称,而不是让 Datastore 自动分配数字 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)
阅读 Google 数据存储后 concepts/theory 我开始使用 Go datastore package
场景:
User
和 LinkedAccount
类型要求每个用户都有一个或多个链接帐户(是的第 3 方登录)。为了高度一致,LinkedAccounts 将是关联用户的子级。然后创建新用户涉及创建一个用户和一个 LinkedAccount,而不仅仅是一个。
用户创建似乎是交易的完美用例。如果说 LinkedAccount 创建失败,则事务回滚失败。这目前看来是不可能的。目标是在事务中创建一个父项,然后创建一个子项。
根据文档
All Datastore operations in a transaction must operate on entities in the same entity group if the transaction is a single group transaction
我们希望新的 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
不是密钥,不完整的密钥不能用作父项。
这是 Datastore 或库的必要限制吗?对于那些有过这种需求的人,你们是不是只是牺牲了强一致性而将这两种需求都变成了全局?
对于Google能力:
- 数据存储:无效密钥
- 数据存储:不能使用 pendingKey 作为类型 *"google.golang.org/cloud/datastore".Key
需要注意的一件事是 Cloud Datastore 中的事务 API can operate on up to 25 entity groups,但这并没有回答如何在同一实体组中创建两个实体作为单笔交易。
有几种方法可以解决这个问题(请注意,这适用于云数据存储 API 的任何使用,而不仅仅是 gcloud-golang
库):
为父键使用(字符串)名称,而不是让 Datastore 自动分配数字 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)