当调用此POST方法时,我们仍然很少有重复条目的情况。
之前,我曾就Stack溢出问题征询过意见,并给了我一个solution,即利用parent/child
方法保留了高度一致的查询。
我已将所有数据迁移到该表单中,并允许其再运行3个月。
但是,问题从未解决。
这个条件if recordsdb.count() == 1:
就在这里
为了更新条目,它应该为true,但是HRD可能并不总是找到最新的条目,而是创建一个新的条目。
如您所见,我们正在按照推荐的方式通过父/子方法从记录中进行写入/读取:
new_record = FeelTrackerRecord(parent=user.key,...)
然而,在检索时,HRD仍不总是获取最新条目:
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
PLUS 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没有理由不再查找该记录。
您认为这是有保证的解决方案吗?
最佳答案
可能的解决方案似乎与原始代码有相同的问题。想象一下,如果两个服务器实际上同时执行相同的指令,就会出现争用情况。由于Google的超额配置,这肯定会偶尔发生。
当并发导致一致性冲突时,更可靠的解决方案应使用Transactions和回滚。用户实体应该是其自己的实体组的父级。在事务内的用户实体中增加记录计数器字段。仅当事务成功完成时,才创建新的FeelTrackerRecord。因此,FeelTrackerRecord实体必须以User作为父级。
编辑:就您的代码而言,以下行将在user = User.query(...之前进行:
Transaction txn = datastore.beginTransaction();
try {
并且以下行将在user.put()之后:
txn.commit();
} finally {
if (txn.isActive()) {
txn.rollback();
}
}
这可能会忽略一些流控制嵌套细节,这是该答案试图描述的概念。
对于活动事务,如果多个进程(例如,由于过多配置而在多个服务器上同时执行同一POST),则第一个进程将成功完成其放置和提交操作,而第二个进程将抛出已记录的ConcurrentModificationException。
编辑2:增加计数器(并可能引发异常)的事务也必须创建新记录。这样,如果引发异常,则不会创建新记录。
关于python - 高复制数据存储中的重复条目,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/22665098/