2010-02-11 121 views
3

考虑一个允许用户评论歌曲的GAE(python)应用程序。预期用户数量为1,000,000+。预计的歌曲数量是5000。如何计算Google App Engine中多对多关系的双方

应用程序必须能够:

  • 给歌曲的用户已经对
  • 评论数给谁有一首歌

计数器管理必须评论的用户数是事务性的,以便它们始终反映基础数据。

看起来,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='[email protected]') 
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() 

的问题是:我怎样才能事务管理两个计数器?

基于我的理解,这将是不可能的,因为用户,歌曲和SongUser必须是相同的entity group的一部分。他们不能在一个实体组中,因为那时我的所有数据都将在一个组中,并且不能由用户分配。

回答

1

你真的不应该担心处理用户在交易中评论过的歌曲的数量,因为用户似乎不太可能一次对多首歌曲发表评论,对吗?

现在,很多用户可能一次对同一首歌曲发表评论,因此您必须担心确保数据不会因竞争条件而失效。但是,如果您保留在Song实体中评论歌曲的用户数量,并用事务锁定实体,则您将争夺该实体的非常高的争用,并且数据存储超时将会使你的应用程序有很多问题。

此问题的答案是。

为了确保您可以创建一个新的SongUser实体并更新相关乐曲的分片计数器,您应该考虑让SongUser实体将相关乐曲作为父项。这将把他们放在同一个实体组中,你可以创建SongUser并在同一个事务中更新分片计数器。 SongUser与创建它的用户的关系可以保存在ReferenceProperty中。

关于您对两个更新(事务型和用户更新)的关注并非都成功,这总是可能的,但是如果更新可能失败,则需要进行适当的异常处理以确保都成功了。这是一个重要的观点:事务内更新不保证成功。如果交易因任何原因无法完成,您可能会收到TransactionfailedError异常。

因此,如果您的事务完成而不引发异常,请在事务中运行用户更新。如果出现一些错误,这将使您自动重新尝试更新到用户。除非我对用户实体有可能的争论有些不明白之处,否则它最终不会成功的可能性是超越小型。如果这是一个不可接受的风险,那么我认为那个AppEngine没有为你解决这个问题的完美解决方案。

首先问问自己:如果某人评论过的歌曲数量已经减少了一个,这真的很糟吗?这与更新银行账户余额或完成股票销售一样重要吗?

+0

您的解决方案可以减少争用,但我真正想要做的是确保两个计数器都匹配底层'SongUser'记录。如果我使用'Song'实体的分片计数器,那么当创建'SongUser'时仍然可以成功,并且增加歌曲的计数器失败(反之亦然)。 – cope360 2010-02-11 15:09:59

+0

已注意。我更新了我的答案以反映您的担忧。 – 2010-02-11 15:55:43

+0

我认为最后一段中的解决方案可能是GAE限制中的最佳选择。在那个解决方案中,我们翻转了我第一个评论的例子。例如,现在可以更新/创建乐曲计数器和SongUser记录,但用户记录更新失败(反之亦然)。你是否同意不可能更新两个计数器(分片还是不分片)? – cope360 2010-02-11 17:34:44

相关问题