Google Cloud Datastore 提交冲突检测

Google Cloud Datastore commit conflict detection

我在数据存储模式下使用 Firestore。我没有使用 Google 或任何人提供的客户端库,而是通过 proto grpc 库(任何人都可以生成的库)生成的(由 Google)直接与 Datastore 通信。我使用它是因为 high-level 客户端不应该使用实体版本和冲突检测机制。

我正在进行事务更新并指定冲突检测策略基础版本(每个实体可能包含以前已知的版本)。

问题是,当任何实体中存在版本冲突时,整个提交操作会失败吗?如果它没有失败,这样的提交是否会应用到数据存储中,我不会使用简单的事务回滚调用进行回滚。

我是否仍需要通过单独调用数据存储在该事务之外实现我自己的独立回滚?

突变产生

承诺并决定下一步做什么

我已经查看了文档,但离完成这个问题还有很长的路要走。这是我唯一找到的。

https://cloud.google.com/datastore/docs/reference/data/rpc/google.datastore.v1#google.datastore.v1.MutationResult

Datastore 对可以在单个事务中完成的内容施加了限制。通常,当存在提交冲突时,并发 transaction/modification 会得到回复。您可以通过启动写入同一文档的两个事务、提交并查看预期行为来重现这一点。

Documentation:

When two or more transactions simultaneously attempt to modify entities in one or more common entity groups, only the first transaction to commit its changes can succeed; all the others will fail on commit. Because of this design, using entity groups limits the number of concurrent writes you can do on any entity in the groups. When a transaction starts, Datastore uses optimistic concurrency control by checking the last update time for the entity groups used in the transaction. Upon committing a transaction for the entity groups, Datastore again checks the last update time for the entity groups used in the transaction. If it has changed since the initial check, an exception is thrown.

最好的方法是快速重现它并查看确切的行为。问题是,当任何实体中存在版本冲突时,整个提交操作是否会失败?

是的。

包装器将自动重试您的交易。 Golang 数据存储 运行 事务库应该提供对其行为方式的更多洞察。有关详细信息,请查看下面的 RunInTransaction 并了解它如何评估交易。

func (c *Client) RunInTransaction(ctx context.Context, f func(tx *Transaction) error, opts ...TransactionOption) (cmt *Commit, err error) {
        ctx = trace.StartSpan(ctx, "cloud.google.com/go/datastore.RunInTransaction")
        defer func() { trace.EndSpan(ctx, err) }()
     
        settings := newTransactionSettings(opts)
        for n := 0; n < settings.attempts; n++ {
     tx, err := c.newTransaction(ctx, settings)
     if err != nil {
     return nil, err
     }
     if err := f(tx); err != nil {
     _ = tx.Rollback()
     return nil, err
     }
     if cmt, err := tx.Commit(); err != ErrConcurrentTransaction {
     return cmt, err
     }
     // Pass this transaction's ID to the retry transaction to preserve
     // transaction priority.
     if !settings.readOnly {
     settings.prevID = tx.id
     }
        }
        return nil, ErrConcurrentTransaction
    }