0

アプリ エンジンをバックエンドとして iOS レーシング ゲームを作成しています。しかし、6月下旬に奇妙なことが起こりました(休暇から戻ったばかりなので、今投稿しています)。

クライアントはスコアを投稿し、サーバーからハイスコアのリストをフェッチし、すべてが完璧に機能しているように見えました (1 か月間テストしましたが、問題はなく、コードは put/get に過ぎず、非常にシンプルです)。しかし、6 月下旬の数時間、古いデータがクライアントに返されました。それはしばらくの間起こりましたが、その後データは自動的に修正されました。

しかし、スコアの送信時に、サーバーがプレーヤーごとに 1 つのハイスコアしか持っていないことを確認するため、これでも問題が発生しましたが、このアプリ エンジンのバグ (?) により、サーバーは一部のプレーヤーに対して複数のスコアを持っていました。

プレーヤー 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 ))

そして、それは正常に機能していましたが、何らかの理由で、数時間古いデータ (数日前のデータなど) を返しました。

4

2 に答える 2

0

クエリを介してスコアを取得している場合は、HRD データストアで結果整合性が発生しています。これは、(クエリに使用される) インデックスが非同期に構築される (= インデックスが構築される前に書き込み操作が返される) ために発生します。

于 2013-07-29T15:03:06.827 に答える
0

クエリを使用して、更新のために 1 人のプレーヤーの記録を取得していますか? 申し訳ありませんが、簡単に読む時間がありますが、これは事実のようです. この種の更新にクエリを使用するべきではないと言っても過言ではありません。結果整合性の問題が発生し、すべての get() は必要以上に非効率的 (レイテンシー) で高コスト (読み取り/書き込み操作) になります。各プレーヤーのハイスコア記録を、そのプレーヤー固有の情報から作成された一意の ID で書き込んでみませんか? 次に、get_by_id()、および put()。一貫性が保証されており、唯一の制限は、この場合は問題にならないレコードごとに 1 秒あたり 1 回の書き込みの制限です。また、私はあなたのように delete() に頼ることを非常に恐れています。get_by_id() プロセスを使用した場合は、各 put() で古い hs を新しい hs で上書きするだけです。また、

于 2013-07-30T15:33:15.230 に答える