A: 

You need a read/write lock on your room lists.

Crashworks
A: 

memcache is 'just' a cache, and in its usual guise it's not suitable for an atomic data store, which is what you're trying to use it for. I'd suggest using the GAE datastore instead, which is designed for this sort of issue.

Kylotan
Yes, but the problem remains if I switch to datastore. GAE can't "read-change-save" in atomic way. The only way to do it is to fetch users, append new user and then put them back to datastore.
Pavel Alexeev
GAE features transactions which are designed to give you the atomicity you need. db.run_in_transaction() should allow you to run your join() function atomically.
Kylotan
+1  A: 

In Memcache, the INCR operation is atomic, and returns the new value incremented. For instance, if a value is set to 0, you can obtain a lock optimistically by incrementing it. If you get by 1, you can safely write a value. If you get back a 2, you should retry the transaction.

http://code.google.com/appengine/docs/python/memcache/functions.html

Ikai Lan
+2  A: 

Something like this may work.

class Room(db.Model):
    users = db.StringListProperty()

def join(userId):
    def _transaction():
        room = Room.get_by_key_name('room_1')
        if room is None:
            room = Room(key_name = 'room_1', users = [])
        room.users.append(userId)
        room.put()
        return room.users
    return db.run_in_transaction(_transaction)
Saxon Druce
Wow! Thank you! I did't know about "run_in_transaction" :)
Pavel Alexeev