App Engine:创建独特实体的机制(如果它不存在)

时间:2015-05-14 08:52:12

标签: google-app-engine transactions google-cloud-datastore

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

我在我的示例中使用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),有两个非错误情况,我可以看到:

  • 获取成功以及'实体组时间戳'在此交易中,可以使用计数器确保没有其他交易更新计数器。
  • 获取失败并显示ErrNoSuchEntity,而Put用于首次存储计数器。

在第二种情况下,可能正在运行另一个相同的事务。如果两个交易都是'获取返回ErrNoSuchEntity数据存储区如何确保只有一个put成功?我希望没有"实体组时间戳"在数据存储区中进行测试?

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

在这种情况下,是否有可能两个交易成功,一个Put覆盖另一个?

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

2 个答案:

答案 0 :(得分:3)

来自documentation on transactions

  

在事务内部,强制执行可序列化隔离。

我建议在链接wikipedia page上阅读一下,但简而言之,数据存储区将确保最终结果就像两个事务按顺序运行一样。

让两个交易都写一个新的,归零的计数器可能的结果。

  

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

以某种方式:只有在没有重叠的交易触及相同的密钥时,交易才能在第一次尝试时成功。

  

在这种情况下,是否有可能两个交易成功,一个Put覆盖另一个?

,如果两个事务时间帧重叠,那么最后一个提交将失败,最终会重试,然后会看到现有的计数器并递增它。

答案 1 :(得分:2)

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

  

获取此引用的快照,并在必要时创建它。

     

如果没有为引用的实体组设置快照,则会拍摄快照并存储以供将来读取(这也会设置读取位置),如果我们不再具有一致的快照,则会引发CONCURRENT_TRANSACTION异常。

因此边缘情况与您推测的情况略有不同:两个事务都会创建一个新的时间戳,然后事情会像往常一样工作。在你提出的情况下,第二个交易将重试,计数器将增加两次。

没有关于交易如何工作的深入文档,至少不是这个深层,但源代码实际上不是 很难读;在这种情况下,您可以跟踪library(mvtnorm) OP <- function(x) { n <- nrow(x) temp <- 0 for(i in 1:n) { for(j in 1:n) { temp <- temp + dmvnorm(x[i,] - x[j,]) } } return(temp) } josilber <- function(x) { n <- nrow(x) mat1 <- x[rep(1:n, each=n),] mat2 <- x[rep(1:n, n),] sum(dmvnorm(mat1-mat2)) } # 100 x 10 matrix set.seed(144) x <- matrix(rnorm(1000), nrow=100) all.equal(OP(x), josilber(x)) # [1] TRUE library(microbenchmark) microbenchmark(OP(x), josilber(x)) # Unit: milliseconds # expr min lq mean median uq max neval # OP(x) 654.553137 696.28275 738.655380 719.058485 760.699813 1194.5594 100 # josilber(x) 2.775881 2.95865 6.789969 4.346013 5.948481 66.0617 100 错误的跟踪。