High Replication数据存储区中的重复条目

时间:2014-03-26 15:06:05

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

调用此POST方法时,我们仍然会遇到重复条目的罕见情况。 我之前曾询问有关堆栈溢出的建议并获得了solution,它使用parent/child方法来保留强一致性查询。

我已将所有数据迁移到该表单中,并让它再运行3个月。 但问题从未解决过。

这个条件if recordsdb.count() == 1:问题就在这里 为了更新条目应该是真的,但是HRD可能并不总是找到最新的条目而是创建一个新的条目。

正如您所看到的,我们按照建议通过父/子方法编写/阅读记录:

new_record = FeelTrackerRecord(parent=user.key,...)

然而,在检索时,人力资源开发司仍然不会总是获取最新的条目:

recordsdb = FeelTrackerRecord.query(ancestor = user.key).filter(FeelTrackerRecord.record_date == ... )

所以我们非常坚持这一点,并且不知道如何解决它。

@requires_auth
    def post(self, ios_sync_timestamp):
        user = User.query(User.email == request.authorization.username).fetch(1)[0]
        if user:
            json_records = request.json['records']
            for json_record in json_records:
                recordsdb = FeelTrackerRecord.query(ancestor = user.key).filter(FeelTrackerRecord.record_date == date_parser.parse(json_record['record_date']))
                if recordsdb.count() == 1:
                    rec = recordsdb.fetch(1)[0]
                    if 'timestamp' in json_record:
                        if rec.timestamp < json_record['timestamp']:
                            rec.rating = json_record['rating']
                            rec.notes = json_record['notes']
                            rec.timestamp = json_record['timestamp']
                            rec.is_deleted = json_record['is_deleted']
                            rec.put()
                elif recordsdb.count() == 0:
                    new_record = FeelTrackerRecord(parent=user.key,
                                        user=user.key, 
                                        record_date = date_parser.parse(json_record['record_date']), 
                                        rating = json_record['rating'], 
                                        notes = json_record['notes'], 
                                        timestamp = json_record['timestamp'])
                    new_record.put()
                else:
                    raise Exception('Got more than two records for the same record date - among REST post')
            user.last_sync_timestamp = create_timestamp(datetime.datetime.today())
            user.put()
            return '', 201
        else:
            return '', 401

可能的解决方案:

我必须解决这个问题的最后一个想法是,摒弃父/子策略并使用user.key加号date-string作为密钥的一部分。

存储

new_record = FeelTrackerRecord(id=str(user.key) + json_record['record_date'], ...)
new_record.put()

装载

key = ndb.Key(FeelTrackerRecord, str(user.key) +  json_record['record_date'])
record = key.get();

现在我可以检查记录是否为None,我将创建一个新条目,否则我将更新它。希望HRD没有理由不再找到记录了。 您怎么看?这是一个有保障的解决方案吗?

1 个答案:

答案 0 :(得分:2)

可能的解决方案似乎与原始代码具有相同的问题。想象一下,如果两台服务器几乎同时执行相同的指令,则会出现竞争情况。由于谷歌过度配置,这肯定会偶尔发生。

更强大的解决方案应该使用Transactions并在并发导致一致性违规时进行回滚。用户实体应该是其自己的实体组的父级。增加事务中User实体中的记录计数器字段。仅在事务成功完成时才创建新的FeelTrackerRecord。因此,FeelTrackerRecord实体必须具有User作为父级。

修改:对于您的代码,以下行将在user = User.query(...:

之前执行
Transaction txn = datastore.beginTransaction();
try {

以下行将在user.put()之后:

    txn.commit();
} finally {
    if (txn.isActive()) {
        txn.rollback();
    }
}

这可能会忽略一些流控制嵌套细节,这是这个答案试图描述的概念。

使用活动事务,如果有多个进程(例如,由于过度配置而在多个服务器上同时执行同一个POST),第一个进程将通过其put和commit成功,而第二个进程将抛出记录的ConcurrentModificationException。

编辑2 :递增计数器的事务(并且可能抛出异常)也必须创建新记录。这样,如果抛出异常,则不会创建新记录。