Reputation: 861
I am using anom to map with GAE datastore entities. I am searching for a scalable way to implement unique constraint on properties.
So far I am checking with queries before creating new records like this
class User(Model):
...
email_id = props.String(indexed=True)
...
def pre_put_hook(self):
if (not self.id_or_name) and (User.query().where(User.email_id == self.email_id).count() > 0):
raise IntegrityError(f'Duplicate entry: User.email == {self.email_id}')
It doesn't handle race conditions and will not force uniqueness on db level. Is there any way to do it without any race conditions?
Upvotes: 1
Views: 1136
Reputation: 861
I have found this snippet from webapp2's repository and modified it for anom
with a mixin to easy integration into any number of models. Now I can confirm the uniqueness of multiple fields inside a single entity. Of-course this suffers extra reads when creating new records and these fields are not checked for uniqueness when updating. So these fields must not be allowed to be updated or a similar check can be done when updating these fields in particular. Here is the gist. I hope this will be useful when using new Python 3 runtime of GAE and anom
as data-mapper
Upvotes: 0
Reputation: 39824
The reason for which your current method is still prone to race conditions is the fact that (non-ancestor) query results are always eventually consistent. Even if you use ancestor queries (the only queries allowed inside a transaction) you may still probably be facing race conditions due to the datastore's Isolation and consistency model.
As Alex mentions, the only way to ensure uniqueness would be to use the property value as the entity's key identifier (you'd be checking the key's existence in your pre_put_hook
method, see TypeError with get_or_insert). But you can only do that for a single property for an entity kind and in particular for the email address property it'd not be a great idea as it'd be difficult to handle email address changes.
There could be ways of enforcing uniqueness, but allowing a temporary/transient potentially duplicate condition, see ndb verify entity uniqueness in transaction.
Upvotes: 1
Reputation: 5276
There's no great way to do it at the db level besides using 'email' as your primary key do user. You'll be relying on your web application code / ORM (whether it be anom
or ndb
) to do the enforcement.
See this answer
composite key in google cloud datastore
Upvotes: 2