【问题标题】:GAE Go - how to handle ErrConcurrentTransaction Datastore Transaction errorGAE Go - 如何处理 ErrConcurrentTransaction 数据存储事务错误
【发布时间】:2014-09-02 14:18:12
【问题描述】:

我正在编写一个 Google App Engine Golang 应用程序。在Datastore Transaction documentation中,有一条说明:

注意:如果您的应用在提交交易时收到错误消息,并不总是意味着交易失败。在事务已提交并最终将成功应用的情况下,您可以收到 ErrConcurrentTransaction。尽可能使您的 Datastore 事务具有幂等性,这样如果您重复事务,最终结果将是相同的。

这让我相信,如果一个事务返回 ErrConcurrentTransaction,这意味着 Datastore 最终将完成该事务。但是,阅读RunInTransaction 我们可以看到一条注释:

如果 f 返回 nil,则 RunInTransaction 尝试提交事务,如果成功则返回 nil。如果由于事务冲突而导致提交失败,则 RunInTransaction 将重试 f,每次都使用新的事务上下文。它在 3 次尝试失败后放弃并返回 ErrConcurrentTransaction

看起来 ErrConcurrentTransaction 是 RunInTransaction 函数的失败状态,这意味着事务永远不会提交。

那么,它是什么?如果 RunInTransaction 返回 ErrConcurrentTransaction,我的代码应该假设什么?交易成功了,以后会成功,还是失败了?

【问题讨论】:

    标签: google-app-engine error-handling transactions go consistency


    【解决方案1】:

    具体场景。考虑以下 sn-p:

    err := datastore.RunInTransaction(c, func(c appengine.Context) error {
        var err1 error
        count, err1 = inc(c, datastore.NewKey(c, "Counter", "singleton", 0, nil))
        return err1
    }, nil)
    // Here, if err is anything other than nil, the datastore-specific
    // operations didn't commit to the datastore. 
    

    当我们运行这个 sn-p 时,这是一种可能的情况:

    1. RunInTransaction 启动。它calls the provided function
      • 在该函数中,inc() 操作返回 nil。
    2. RunInTransaction 收到该结果,并尝试完成提交。但由于某种原因,它失败了。 (Where it can fail.) 所以 RunInTransaction 从数据存储区接收到 ErrConcurrentTransaction。
    3. RunInTransaction tries again。它调用提供的函数。
      • 在该函数中,inc() 操作返回 nil。
    4. RunInTransaction 收到该结果,并尝试完成提交。但由于某种原因,它又一次失败了。嘿,也许它很忙。
    5. RunInTransaction 耐心地再次尝试该函数。
      • 在该函数中,inc() 操作返回 nil。
    6. RunInTransaction 收到该结果,并尝试完成提交。这一次,底层数据存储允许提交。万岁!
    7. RunInTransaction 向其调用者返回 nil。

    因此,在这种情况下,您的应用程序会观察 ErrConcurrentTransactions。您阅读的第一个注释是关于整个系统的一般性评论:作为一个整体,您的程序可能会遇到 ErrConcurrentTransactions。但这并不意味着您编写的代码会直接触及 ErrConcurrentTransaction。您的代码可能根本看不到此错误。然而 RunInTransaction 正在代表您的代码运行,并且 RunInTransaction 可能会看到该错误。但是事务仍然可以继续进行,因为 RunInTransaction 将重播该函数,直到它成功,或者数据存储足够忙而放弃。

    如果您从 RunInTransaction 获得 nil 作为最终返回值,则数据存储操作已完成。但如果你得到非零,他们没有。

    请注意,在上述场景中,RunInTransaction 调用的函数作为重试协议的一部分被多次调用。因此,您必须确保传递给 RunInTransaction 的函数没有问题,因为它在数据存储繁忙时尝试使用重试。

    【讨论】:

    • 多么棒的答案。详细解释。
    猜你喜欢
    • 1970-01-01
    • 2013-02-16
    • 1970-01-01
    • 2013-09-07
    • 1970-01-01
    • 1970-01-01
    • 2023-03-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多