我们有一个问题,如果它不存在,我们想懒惰地创建一个实体。有一些关于如何做到这一点的讨论,我想澄清一些关于应用引擎交易的事情。我将我的查询限制为单个实体组事务。

我在示例中使用 Go,但我希望代码对于非 Go 程序员来说足够清晰。

我的理解是,单个实体组上的事务只有在事务期间未从外部修改实体组时才会成功。指示实体组何时更改的“实体组时间戳”存储在实体组的根实体中。因此,在事务期间,当前的“实体组时间戳”被读取,并且只有在事务结束时它没有改变,事务才能成功。

key := datastore.NewKey(c, "Counter", "mycounter", 0, nil)
count := new(Counter)
err := datastore.RunInTransaction(c, func(c appengine.Context) error {
  err := datastore.Get(c, key, count)
  if err != nil && err != datastore.ErrNoSuchEntity {
    return err
  }
  count.Count++
  _, err = datastore.Put(c, key, count)
  return err
}, nil)

在上面的例子中(取自 https://cloud.google.com/appengine/docs/go/datastore/transactions )有两种非错误情况,我可以看到:
  • Get 成功并且可以使用计数器上的“实体组时间戳”来确保在此事务期间没有其他事务更新计数器。
  • Get 失败,ErrNoSuchEntity 和 Put 用于第一次存储计数器。

  • 在第二种情况下,另一个相同的事务可能正在运行。如果两个事务的 Get 都返回 ErrNoSuchEntity 数据存储如何确保只有一个 put 成功?我希望数据存储中没有要测试的“实体组时间戳”?

    交易是否知道它需要测试计数器是否不存在才能使 Put 和整个交易成功?

    在这种情况下,是否有机会让两个交易成功并且一个 Put 覆盖另一个?

    如果有关于控制它的机制的文档或视频等,我很乐意阅读它。

    最佳答案

    要回答您的问题,我们必须深入挖掘开发数据存储的源代码,幸运的是,它有很好的文档记录,只需看看 LiveTxn._GrabSnapshot :

    因此,边缘情况与您推测的情况略有不同:两个事务都将创建一个新的时间戳,然后事情会像往常一样工作。在您提出的情况下,第二个事务将重试,并且计数器将增加两次。
    没有我所知道的关于事务如何工作的深入文档,至少没有这么深,但源代码实际上并不难阅读;在这种情况下,您可以跟踪 CONCURRENT_TRANSACTION 错误的踪迹。

    关于google-app-engine - 应用引擎 : Mechanics of creating a unique entity if it doesn't exist,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/30233093/

    10-16 05:43