Domanda

I am trying to create a simple register for users. In order for that register to be consistent, i cannot allow registering two users with the same username, on account to the eventual consistency of the default queries. I know the odds of that happening even with considerable traffic are almost 0, i want my implementation to be solid. I have implemented the ancestor queries that are supposed to be strongly consistent, yet when i publish my app and test it, i can make it register 2 users with the same username if i time it right.

def user_parent(group = 'default'):
key = ndb.Key('users', group)
return key

class User(ndb.Model):
    username = ndb.StringProperty(required = True)
    email = ndb.StringProperty(required = True)
    password = ndb.StringProperty(required = True)

@classmethod
def register(cls, username, password, email):
    return User(parent = user_parent(),
                username = username,
                password = password,
                email = email)

@classmethod
def by_name(cls, name):
    return User.query(User.username == name, ancestor = user_parent()).get()

@classmethod
def by_id(cls, uid):
    return User.get_by_id(uid, parent = user_parent()) 

@classmethod
def by_email(cls, email):
    logging.error(User.query(User.email == email, ancestor = user_parent()).get())
    return User.query(User.email == email, ancestor = user_parent()).get()

@classmethod
def login(cls, user, password):
    u = cls.by_name(user)
    if u and u.password == password:
        return u

I use reg_submit to perform an ajax request, i'm very new to programming and doubt this is the best solution to go about it, but the idea is that here i validate with calls made to the User model class.

    if reg_submit == 'True':
        reg_email = self.request.get('reg_email')
        reg_user = self.request.get('reg_user')
        reg_password = self.request.get('reg_password')
        reg_verify = self.request.get('reg_verify')

        valid = True
        if not valid_usr(reg_user):
            valid = False
            self.write('user')
            return
        if not valid_mail(reg_email):
            valid = False
            self.write('mail')
            return

        if User.by_email(reg_email):
            valid = False
            self.write('mail')
            return

        if User.by_name(reg_user):
            valid = False
            self.write('user')
            return

        if not valid_pass(reg_password):
            valid = False

        if reg_password != reg_verify:
            valid = False

        if valid:
            t = User.register(reg_user, reg_password, reg_email)
            t.put()
            self.login(t)
            self.redirect('/')

What i understand by making a query strongly consistent, is that the said query will not execute until every machine on which my database is replicated will be updated.

How do i achieve strongly consistent queries where two users, registering at the same time, with the same username, one of them, will not be able to do so.

È stato utile?

Soluzione

You need to run the check-query and the put inside a transaction. The docs for doing this with NDB are here.

Altri suggerimenti

Model.get_or_insert (key_name, **kwds)

Attempts to get the entity of the model's kind with the given key name. If it exists, get_or_insert() simply returns it. If it doesn't exist, a new entity with the given kind, name, and parameters in kwds is created, stored, and returned.

The get and subsequent (possible) put operations are wrapped in a transaction to ensure atomicity. Ths means that get_or_insert() will never overwrite an existing entity, and will insert a new entity if and only if no entity with the given kind and name exists.

Get Or Insert.

Use the username as the key_name. Set your model up so it has a default, say "new = True" and check that when you get the model back to see if it was just created or was already there when you asked for it. When you config the model and save it back, set "new = False".

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top