AppEngine Python - how to ensure that users regist

2019-08-15 06:31发布

I am designing a new AppEngine/Python HRD application. In this application, I need to ensure that each user that registers cannot use a name that has already been assigned to another user, and that if two users attempt to register with the same username at the exact same moment, that only one of them will be given the username.

If I were to put all usernames in a single entity group and use transactions to ensure uniqueness, then the writing of the username/object in a transaction could potentially slow down all registration processes. So this approach doesn't seem like a good idea.

Another option would be to use the username as the key, which has the disadvantage of possibly making it difficult for the user to later change their username.

What is the best/standard approach to achieve this in AppEngine?

2条回答
贼婆χ
2楼-- · 2019-08-15 07:25

App Engine allows up to 1 write per second within the same entity group. To surpass that barrier you have to have 60 x 60 x 24 = 86.4k registrations on the same day, which I think you can live with.

If you still think you need any faster process you could use sharding (more typically known for counters - https://developers.google.com/appengine/articles/sharding_counters), which is basically having different tables for users (or usernames) so that you can read them all to check for uniqueness (App Engine is good at doing many reads per second) and have as much better write pace as tables for usernames you add.

More of sharding on writes here - https://developers.google.com/appengine/articles/scaling/contention

Hope it helps.

查看更多
该账号已被封号
3楼-- · 2019-08-15 07:32

Ok.

You don't need to stick all the usernames in a big entity group to guarantee consistency.

Make the username the Key of the datastore entity governing the login.

Then inside a transaction.

  1. Try a get using the username as a key (this is a consistent operation) if you find it then obviously it's not available
  2. If not found then create the new login entity.

As an aside if you used email addresses then it would more than likely mean no clashes ever. and I am not sure why visible nick names need to be unique, but then you probably have a good reason.

Each actual user object can have a system generated unique id, (this is a separate entity to the login entity).

If you are really paranoid, then look at using memcache CAS operations to effectively act as a lock on the username key and prevent simultaneous operations, though I don't think it would be necessary.

Entities might look like

class Login(ndb.Model):
    # other stuff neede for authentication etc..
    user = ndb.KeyProperty(User)

    @ndb.transactional(xg=True)
    @classmethod
    def create_login(cls,username):
        # maybe pass in extra user details
        key = ndb.Key(cls, keyname)
        login = key.get()
        if login:
            raise DuplicateUsernameError(username)

        login = Login(key_name=username)
        user =  User.create_user(login=login.key)
        login.user = user.key
        login.put()
        return (login,user) 


class User(ndb.Model):
    login = ndb.KeyProperty(Login)  # this property is only for convenience.
    nickname = ndb.StringProperty()
    # etc

    @classmethod
    def create_user(cls,login_key):
        # yes you would pass in more user stuff.

        user = cls(login=login_key)
        user.put()

        # the unique user key is system generated.
        return user

This means only a single get() is required to fetch a login, and second to get the rest of the user details but is much cheaper/faster than queries. It also means that the login name/code could be changed over time without disrupting the actual user entity if you really wanted such functionality. Or possibly support multiple login methods for a single user - ie facebook and twitter. It also means a login entity can be removed and login re-used by other people over time, and the user entity can stay if system integrity/history is required.

查看更多
登录 后发表回答