Consider a GAE (python) app that lets users comment on songs. The expected number of users is 1,000,000+. The expected number of songs is 5,000.
The app must be able to:
- Give the number of songs a user has commented on
- Give the number of users who have commented on a song
Counter management must be transactional so that they always reflect the underlying data.
It seems GAE apps must keep these types of counts calculated at all times since querying for them at request time would be inefficient.
My Data Model
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
This handles the user's song count transactionally but not the song's user count.
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()
The question is: How can I manage both counters transactionally?
Based on my understanding this would be impossible since User, Song, and SongUser would have to be a part of the same entity group. They can't be in one entity group because then all my data would be in one group and it could not be distributed by user.