Google App Engine分类计数器无效,获取重复数字

时间:2011-09-04 16:56:01

标签: google-app-engine

我正在使用Google App Engine编写一个错误数据库,而我遇到的问题是获取错误的唯一编号。每个错误都需要一个唯一的编号,以便用户可以轻松地引用它们,数字应该简单,尽可能小。 “嘿,我修复了错误27”或“我重新打开了错误1867”。错误号码也应该增加,这样用户就可以粗略地了解哪些错误发生在哪个错误之后。

App Engine没有像SQL这样的真实计数器,所以我实现了以下功能,这基本上是谷歌推荐的代码,但不能一直运行。

我偶尔会看到重复的错误号码。目前我是唯一一个使用bug数据库的人(图标上,这个bug在我的bug数据库中)并且我没有每隔5或10秒(如果我快速键入)更快地输入错误。虽然最终会有多个用户可能同时输入错误。

class SimpleCounterShard(db.Model):
    count = db.IntegerProperty(required=True, default=0)

def getNewID():
    def txn():
        index = random.randint(0, NUM_SHARDS - 1)
        shard_name = "shard" + str(index)
        counter = SimpleCounterShard.get_by_key_name(shard_name)
        if counter is None:
            counter = SimpleCounterShard(key_name=shard_name)
        counter.count += 1
        counter.put()
    db.run_in_transaction(txn)

    total = 0
    for counter in SimpleCounterShard.all():
        total += counter.count

    return total

我做错了什么(或不理解)?或者更好的原因是为什么在生产服务器上获得不仅像Key或ID那样随机的唯一数字。

2 个答案:

答案 0 :(得分:1)

分片计数器将具有完全相同的问题 - 如果您可以将其称为 - 内置自动生成的ID具有:它们不保证单调性。分片计数器旨在让您对事物进行计数,而不是让您为事物分配数字;因此,您无法以事务方式获取所有分片的总和,并且结果是错误的。

你真的应该使用内置的自动编号;我怀疑用户是否非常注意相互之间的错误号码的大小,如果你希望它们能够扩展到高写入速率,你可以提出的大多数解决方案会有类似的问题。

如果绝对必须有序列号,则可以使用单个计数器实体,并允许每秒1-10的最大插入速率,或者您可以启动一个“计数器”后端,从数据存储区批量分配ID将它们交给你的前端的RPC请求。如果后者听起来很熟悉,那就是因为这就是自动生成的ID所做的事情,只能在多台机器上进行分片。

答案 1 :(得分:0)

您的代码并非真正的线程安全。是的,您使用事务来增加计数器,但您不使用它来读取计数器。您还没有锁定该事务的其他分片(因为它们位于不同的实体组中)。您可以想象有两个请求会增加计数器,然后在事务之外读取值(获取相同的值)。 你基本上需要在事务中做所有事情并且只有一个分片。这意味着阻止多次调用新id(序列化它们)。我建议为每个项目/标签/上下文/使用不同的计数器来更好地扩展写入。

详细了解交易here

老实说,如果我是你,我只会使用自动生成的模型ID。