Question

Considérons une application GAE (python) qui permet aux utilisateurs des commentaires sur les chansons. Le nombre attendu d'utilisateurs est 1.000.000. Le nombre prévu de chansons est 5000.

L'application doit pouvoir:

  • Donnez le nombre de chansons un utilisateur a commenté
  • Indiquez le nombre d'utilisateurs qui ont commenté une chanson

La gestion du compteur doit être transactionnel afin qu'elles reflètent toujours les données sous-jacentes.

Il semble applications GAE doit garder ces types de comptes calculés à tout moment depuis l'interrogation pour eux au moment de la demande serait inefficace.

Mon modèle de données

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)

code Cela gère le nombre de chansons de l'utilisateur transactionnellement mais pas le nombre d'utilisateurs de la chanson.

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

La question est: Comment puis-je gérer les compteurs transactionnellement

D'après ma compréhension que ce serait impossible puisque l'utilisateur, Song et SongUser devrait être une partie de la même groupe d'entités . Ils ne peuvent pas être dans un groupe d'entités car alors toutes mes données seraient dans un groupe et il n'a pas pu être distribués par l'utilisateur.

Était-ce utile?

La solution

Vous devriez vraiment pas à vous soucier de la manipulation du décompte de l'utilisateur des chansons sur lesquelles ils ont formulé des commentaires dans une transaction car il semble peu probable qu'un utilisateur serait en mesure de commenter plus d'une chanson à la fois, non?

Maintenant, il est certainement le cas que de nombreux utilisateurs pourraient être commentent la même chanson à un moment donné, c'est donc là que vous avez à vous soucier de faire en sorte que les données ne sont pas invalidé par une condition de course.

Cependant, si vous gardez le compte du nombre d'utilisateurs qui ont commenté une chanson dans l'entité Song, et verrouiller l'entité avec une transaction, vous allez obtenir affirmation très élevé pour cette entité et datastore les délais d'attente feront votre application ont beaucoup de problèmes.

Cette réponse à ce problème est Compteurs fragmentées.

Afin de vous assurer que vous pouvez créer une nouvelle entité SongUser et mettre à jour contre de la chanson fragmentées connexes, vous devriez envisager d'avoir l'entité SongUser ont la chanson associée en tant que parent. Cela les mettre dans le même groupe d'entités et vous pouvez créer la sûreté SongUser et mis à jour le compteur de la même fragmentées transaction. La relation de SongUser à l'utilisateur qui l'a créé peut être tenue dans un ReferenceProperty.

En ce qui concerne votre préoccupation au sujet des deux mises à jour (celui transactionnel et la mise à jour de l'utilisateur) pas les deux suivantes, qui est toujours une possibilité, mais étant donné que soit mise à jour peut échouer, vous aurez besoin d'avoir une bonne manutention exception à faire en sorte que les deux réussir. C'est un point important: les mises à jour en-transaction ne sont pas garantis pour réussir. Vous pouvez obtenir une exception TransactionfailedError si la transaction ne peut pas terminer pour une raison quelconque.

Donc, si votre transaction se termine sans soulever une exception, exécutez la mise à jour utilisateur dans une transaction. Cela vous obtiendrez de reprise automatique la mise à jour utilisateur, si une erreur se produit. À moins qu'il ya quelque chose au sujet de discorde possible sur l'entité utilisateur que je ne comprends pas, la possibilité que ce ne sera pas fini par réussir est incomparablement petit . Si tel est un risque inacceptable, alors je ne pense pas que ce AppEngine a une solution parfaite à ce problème pour vous.

Tout d'abord vous demander: est-il vraiment si mauvais si le nombre de chansons que quelqu'un a commenté est désactivé par un? Est-ce aussi important que la mise à jour d'un solde de compte bancaire ou de compléter une vente d'actions?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top