Pergunta

Considere um aplicativo GAE (python) que permite aos usuários comentário sobre canções. O número esperado de usuários é 1.000.000. O número esperado de músicas é 5.000.

O aplicativo deve ser capaz de:

  • Dê o número de músicas que um usuário comentou
  • Dê o número de usuários que têm comentado sobre uma canção

Gestão de Contador deve ser tão transacional que eles sempre refletem os dados subjacentes.

Parece aplicativos GAE deve manter estes tipos de contagens calculadas em todos os momentos, desde a consulta para eles no momento do pedido seria ineficiente.

Meu Modelo de Dados

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)

Código
Este alças contagem de músicas do usuário transactionally mas não contagem de usuários da canção.

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()

A questão é:? Como posso gerir ambos os contadores transactionally

Com base na minha compreensão de que isso seria impossível, já que usuário, Song, e SongUser teria que ser uma parte da mesma grupo de entidades . Eles não podem estar em um grupo de entidades, porque então todos os meus dados estaria em um grupo e não poderia ser distribuído pelo usuário.

Foi útil?

Solução

Você realmente não deveria ter que se preocupar sobre como lidar com a contagem do usuário de canções em que eles têm comentado dentro de uma transação, porque parece improvável que um usuário seria capaz de comentário em mais de uma música de cada vez, certo?

Agora, é definitivamente o caso que muitos usuários poderiam ser comentando sobre a mesma música ao mesmo tempo, de modo que é onde você tem que se preocupar com certificando-se de que os dados não é feita inválida por uma condição de corrida.

No entanto, se você manter a contagem do número de usuários que têm comentado sobre uma canção dentro da entidade Song, e trancar a entidade com uma transação, você vai ficar muito alta contenção para essa entidade e armazenamento de dados tempos de espera vai fazer sua aplicação tem um monte de problemas.

Esta resposta para este problema é Sharded Contadores .

A fim de se certificar de que você pode criar uma nova entidade SongUser e atualizar balcão sharded da Canção relacionados, você deve considerar ter a entidade SongUser ter a Canção relacionado como um pai. Isso vai colocá-los no mesmo grupo de entidades e você pode tanto criar o SongUser e actualizado o contador sharded na mesma transação. O relacionamento da SongUser para o usuário que o criou pode ser realizada em um ReferenceProperty.

No que diz respeito a sua preocupação com as duas atualizações (o transacional e a atualização do usuário) não tanto sucesso, que é sempre uma possibilidade, mas dado que ambos os atualização pode falhar, você precisará ter adequada exceção de manipulação para garantir que tanto suceder. Esse é um ponto importante: a in-transação-atualizações não são garantia de sucesso. Você pode obter uma exceção TransactionfailedError se a transação não pode ser concluída por qualquer motivo.

Assim, se seus concluída transação sem levantar uma exceção, executar a atualização para o usuário em uma transação. Isso vai te tentativas automáticas da atualização para o usuário, deve ocorrer algum erro. A menos que haja alguma coisa sobre possível contenção na entidade de usuário que eu não entendo, a possibilidade de que não irá suceder é surpassingly pequena . Se isso é um risco inaceitável, então eu não acho que isso AppEngine tem uma solução perfeita para esse problema para você.

Primeiro pergunte-se: é realmente tão ruim se a contagem de canções que alguém tenha comentado é off por um? É este tão crítica como a atualização de um saldo de conta bancária ou completar uma venda de ações?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top