GAE交易失败和幂等性

时间:2013-07-18 11:13:50

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

Google App Engine文档包含以下段落:

  

注意:如果您的应用程序在提交时收到异常   交易,并不总是意味着交易失败。您   可以收到DatastoreTimeoutException,   ConcurrentModificationException或DatastoreFailureException   在已经提交交易的情况下的例外情况   最终将成功应用。尽可能让你的   数据存储区事务是幂等的,这样如果重复一个事务,   最终结果将是相同的。

等等,什么?似乎有一类非常重要的事务只是简单地不能成为幂等的,因为它们依赖于当前的数据存储状态。例如,一个简单的计数器,如同一个按钮。事务需要读取当前计数,递增计数并再次写出计数。如果交易看起来“失败”但没有真正失败,并且我无法在客户端告诉我,那么我需要再次尝试,这将导致一次点击产生两个“喜欢”。当然有一些方法可以通过GAE来防止这种情况发生?

编辑:

似乎这是分布式系统中固有的问题,根据Guido van Rossum以外的其他问题 - 请看这个链接:

app engine datastore transaction exception

因此,如果您想要高度可靠性,那么设计幂等事务几乎是必须的。

我想知道是否有可能在整个应用程序中实施全局系统以确保幂等性。关键是在数据存储区中维护事务日志。客户端将生成GUID,然后将该GUID包含在请求中(在重试同一请求时将重新发送相同的GUID)。在服务器上,在每个事务开始时,它将在数据存储区中查找具有该ID的Transactions实体组中的记录。如果它找到了,那么这是一个重复的事务,所以它会在没有做任何事情的情况下返回。

当然,这需要启用跨组事务,或者将单独的事务日志作为每个实体组的子级。如果失败的实体密钥查找速度很慢,那么性能也会受到影响,因为几乎每个事务都包含失败的查找,因为大多数GUID都是新的。

就额外数据存储交互方面的额外$ cost而言,这可能仍然小于我必须使每个事务都是幂等的,因为这需要大量检查每个级别的数据存储区中的内容。 / p>

3 个答案:

答案 0 :(得分:7)

dan wilkerson,simon goldsmith,et al。在app引擎的本地(每个实体组)事务之上设计了一个彻底的global transaction system。在高级别,它使用类似于您描述的GUID的技术。 dan涉及“潜艇写入”,即您描述的报告失败但稍后表现为成功的事务,以及数据存储的许多其他理论和实践细节。 erick armbrust在tapioca-orm中实现了dan的设计。

我不一定建议您实施他的设计或使用木薯粉,但您肯定对该研究感兴趣。

回答您的问题:很多人实施使用数据存储区的GAE应用程序而没有幂等性。只有在您需要某些类型的保证(例如您描述的保证)的交易时,这才是最重要的。了解你何时需要它们是非常重要的,但你通常不需要。

数据存储区是在megastore之上实现的,深度为in this paper。简而言之,它在每个实体组中使用multi-version concurrency control并在数据中心内使用Paxos进行复制,这两者都可以为潜艇写入做出贡献。我不知道数据存储区中的潜艇写入频率是否有公共号码,但如果有,则使用这些条款和数据存储邮件列表进行搜索应该找到它们。

亚马逊的S3并不是一个真正可比的系统;它更像是CDN而不是分布式数据库。亚马逊的SimpleDB具有可比性。它最初只提供eventual consistency,并最终添加了一种非常有限的交易,他们称之为conditional writes,但它没有真正的交易。其他NoSQL数据库(redis,mongo,couchdb等)在事务和一致性方面有不同的变化。

基本上,分布式数据库在规模,事务宽度和一致性保证强度之间总是存在权衡。这是eric brewer的CAP theorem最为人所知的,它表示权衡的三个轴是一致性,可用性和分区容差。

答案 1 :(得分:1)

我想出使计数器幂等的最佳方法是使用集合而不是整数来计算。因此,当一个人喜欢"一些东西,而不是增加一个计数器,我添加像这样的东西:

class Thing {
Set<User> likes = ....

public void like (User u) {
  likes.add(u);
}
public Integer getLikeCount() {
  return likes.size();
}
}

这是在java中,但我希望即使你使用python也能得到我的观点。

此方法是幂等的,您可以添加一个用户多少次,只计算一次。当然,它存在一个巨大的集合,而不是一个简单的计数器的惩罚。但是,嘿,你不需要跟踪喜欢的东西吗?如果您不想膨胀Thing对象,请创建另一个对象ThingLikes,并在Thing对象上缓存相似的数量。

答案 2 :(得分:0)

值得关注的另一个选项是app engine内置cross-group transaction支持,它允许您在单个数据存储区事务中最多运行五个实体组。

如果您更喜欢阅读堆栈溢出,this SO question有更多详细信息。