3

ユーザーが曲にコメントできるGAE(Python)アプリを考えてみましょう。予想されるユーザー数は1,000,000人以上です。予想される曲数は5,000曲です。

アプリは次のことができる必要があります。

  • ユーザーがコメントした曲の数を教えてください
  • 曲にコメントしたユーザーの数を教えてください

カウンター管理は、基礎となるデータを常に反映するようにトランザクション型である必要があります。

GAEアプリは、リクエスト時にクエリを実行すると非効率になるため、これらのタイプのカウントを常に計算しておく必要があるようです。

私のデータモデル

class Song(BaseModel):
    name = db.StringProperty()
    # Number of users commenting on the song
    user_count = db.IntegerProperty('user count', default=0, required=True)
    date_added = db.DateTimeProperty('date added', False, True)
    date_updated = db.DateTimeProperty('date updated', True, False)

class User(BaseModel):
    email = db.StringProperty()
    # Number of songs commented on by the user
    song_count = db.IntegerProperty('song count', default=0, required=True)
    date_added = db.DateTimeProperty('date added', False, True)
    date_updated = db.DateTimeProperty('date updated', True, False)

class SongUser(BaseModel):
    # Will be child of User
    song = db.ReferenceProperty(Song, required=True, collection_name='songs')
    comment = db.StringProperty('comment', required=True)
    date_added = db.DateTimeProperty('date added', False, True)
    date_updated = db.DateTimeProperty('date updated', True, False)

コード
これは、ユーザーの曲数をトランザクションで処理しますが、曲のユーザー数は処理しません。

s = Song(name='Hey Jude')
s.put()

u = User(email='me@example.com')
u.put()

def add_mapping(song_key, song_comment, user_key):
    u = User.get(user_key)

    su = SongUser(parent=u, song=song_key, song_comment=song_comment, user=u);
    u.song_count += 1

    u.put()
    su.put()

# Transactionally add mapping and increase user's song count
db.run_in_transaction(add_mapping, s.key(), 'Awesome', u.key())

# Increase song's user count (non-transactional)
s.user_count += 1
s.put()

問題は 、両方のカウンターをトランザクションで管理するにはどうすればよいですか?

私の理解では、User、Song、SongUserは同じエンティティグループの一部である必要があるため、これは不可能です。すべてのデータが1つのグループになり、ユーザーが配布できなかったため、1つのエンティティグループに含めることはできません。

4

1 に答える 1

1

ユーザーが一度に複数の曲にコメントできる可能性は低いと思われるため、トランザクション内でユーザーがコメントした曲の数を処理することを心配する必要はありません。

さて、多くのユーザーが一度に同じ曲にコメントする可能性があるのは間違いないので、競合状態によってデータが無効にならないように注意する必要があります。

ただし、Songエンティティ内の曲にコメントしたユーザーの数をカウントし、トランザクションでエンティティをロックすると、そのエンティティの競合が非常に高くなり、データストアのタイムアウトによってアプリケーションが多くの問題。

この問題に対するこの答えは、シャードカウンターです。

新しいSongUserエンティティを作成し、関連するSongのシャードカウンターを更新できるようにするには、SongUserエンティティに関連するSongを親として持たせることを検討する必要があります。これにより、それらは同じエンティティグループに配置され、同じトランザクションでSongUserを作成し、シャーディングされたカウンターを更新することができます。SongUserとそれを作成したユーザーとの関係は、ReferencePropertyに保持できます。

2つの更新(トランザクション更新とユーザー更新)の両方が成功しないという懸念については、常に可能性がありますが、どちらかの更新が失敗する可能性があることを考えると、両方が成功するように適切な例外処理を行う必要があります。これは重要なポイントです。トランザクション内の更新が成功することは保証されていません。何らかの理由でトランザクションを完了できない場合は、TransactionfailedError例外が発生する可能性があります。

したがって、例外を発生させずにトランザクションが完了した場合は、トランザクションでUserへの更新を実行します。これにより、エラーが発生した場合に、ユーザーへの更新が自動的に再試行されます。私が理解していないユーザーエンティティでの競合の可能性について何かがない限り、それが最終的に成功しない可能性は非常に小さいです。それが許容できないリスクである場合、AppEngineがこの問題に対する完璧な解決策を持っているとは思いません。

まず、自問してみてください。誰かがコメントした曲の数が1つ減ったら、本当に悪いのでしょうか。これは、銀行口座の残高を更新したり、株式の売却を完了したりするのと同じくらい重要ですか?

于 2010-02-11T14:39:59.593 に答える