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.
You need to run the check-query and the put inside a transaction. The docs for doing this with NDB are here.