我们正在使用应用引擎制作iOS赛车游戏作为后端。然而在六月下旬发生了一些奇怪的事情(我刚从假期回来,因此我现在正在发帖)。
客户端发布得分并从服务器获取高分列表,所有内容似乎都运行良好(我们已经测试了一个月没有任何问题,代码非常简单,只需要一个put / get)。但在6月下旬,几个小时后,旧数据被返回给客户端。它发生了一段时间但随后数据自行修复。
然而,这仍然让我们陷入麻烦,因为在分数提交时我们检查服务器每个玩家只有一个高分,但是这个应用引擎错误(?)导致服务器对某些玩家有多个分数。
所以发生的事情是:玩家A提交分数,玩家B提交分数,数据在只有玩家A存在时恢复到,玩家B提交新分数(它被存储为服务器没有看到玩家B),服务器修复数据问题,现在我们要玩家B。
如果您希望能够依赖应用引擎的后端,您应该怎么做?这可能是一个交易破坏者。
更具体(根据评论中的要求)
我只是在做我们正在做的事情。但基本上它是一样的。所以我们不仅要存储高分,还要存储玩家的幽灵数据。这是代码(但删除了一些现在不相关的额外字段)。
这是模型(剥离了一些非重要字段):
class Highscore(db.Model):
player = db.StringProperty()
track = db.IntegerProperty()
track_time = db.FloatProperty()
ghost_data = blobstore.BlobReferenceProperty()
为了存储,我先做一个:
class GhostPrepareHandler(webapp2.RequestHandler):
def get(self):
self.response.headers['Content-Type'] = 'application/json'
self.response.out.write(json.dumps({ 'add_highscore_url' : blobstore.create_upload_url('/api/highscore') }))
然后
class HighscoreUploadHandler(blobstore_handlers.BlobstoreUploadHandler):
def post(self):
# Check any previous highscore
track = self.request.get('track')
player = self.request.get('player')
hs = Highscore.all().filter('track =', track).filter('player =', player).get()
# Check if a previous ghost exists with a worse time, if so remove it,
# else if previous time is better, do not store this highscore
if hs is not None:
if hs.track_time < float( self.request.get('track_time') ):
self.response.headers['Content-Type'] = 'application/json'
self.response.out.write(json.dumps({ 'success' : True }))
return
hs.delete()
# Store highscore
hs = Highscore()
hs.player = self.request.get('player')
hs.track = int( self.request.get('track') )
hs.track_time = float( self.request.get('track_time') )
upload = self.get_uploads()[0]
hs.ghost_data = upload.key()
# Store
hs.put()
self.response.headers['Content-Type'] = 'application/json'
self.response.out.write(json.dumps({ 'success' : True }))
然后我们得到了阅读部分
def get(self):
player = self.request.get('player')
track = self.request.get('track')
highscores = HighScore.all().filter('track =', track).order('track_time').fetch(limit=4)
highscores_json = []
hs_count = 0
for hs in highscores:
# Filter out player or last ghost
if hs.player == player or hs_count > 2:
continue
hs_obj = {
'player' : hs.player,
'track_time' : hs.track_time,
'ghost_data_url' : 'http://' + host + '/api/highscore/download?ghost_key=' + str( hs.ghost_data.key() )
}
highscores_json.append( hs_obj )
hs_count += 1
self.response.headers['Content-Type'] = 'application/json'
self.response.out.write(json.dumps( highscores_json ))
它一直运行良好,但出于某种原因,几个小时后它返回旧数据(如天数据)。
答案 0 :(得分:0)
如果您通过查询检索分数,那么您遇到了eventual consistency od HRD数据存储区。这是因为索引(用于查询)是异步构建的(=写入操作在构建索引之前返回)。
答案 1 :(得分:0)
您是否正在使用查询来获取一位玩家的更新记录?对不起,只是有时间快速阅读,但似乎是这种情况。我认为可以安全地说你永远不应该对这种类型的更新使用查询。您将遇到最终一致性问题,并且每个get()的效率都比低效率(延迟)和高成本(读/写操作)要大。为什么不用他/她特定信息中的唯一ID来写每个玩家的高分记录?然后是get_by_id()和put()。保证一致性,唯一的限制是每条记录每秒写入一次的限制,在这种情况下不应该是一个问题。另外,我会非常谨慎地依赖于delete()。如果你使用了get_by_id()过程,那么只需用每个put()覆盖旧的hs和新的hs。同样,如果你使用这个过程来维护每个玩家独特的高分记录,你真的必须重新设计它。