GAE Transaction in root entity

2019-07-08 10:45发布

I'm new to GAE and I have some questions about transaction with the DataStore.

For example, I have a user entity, which is created when the user adds my app on Facebook. I get some properties with the Facebook API, but I want to add a username for the user, and it needs to be unique. So in the transaction scope I call this method:

def ExistsUsernameToDiferentUser(self, user, username):
    query = User.all()
    query.filter("username", username)
    query.filter("idFacebook != ", user.idFacebook)
    userReturned = query.get()
    return True if userReturned else False

But GAE gives me this error:

BadRequestError: queries inside transactions must have ancestors

Ok, I understand, but the user doesn't have any ancestor, it's a root entity. What do I have to do?

2条回答
贪生不怕死
2楼-- · 2019-07-08 11:13

I see what you're trying to do now.

By forcing the use of ancestors, the datastore forces you to lock down a portion of the datastore (everything under the given ancestor) so you can guarantee consistency on that portion. However, to do what you want, you essentially need to lock down all User entities to query whether a certain one exists, and then create a new one, and then unlock them.

You CAN do this, just create an entity, it can be an empty entity, but make sure it has a unique key (like "user-ancestor"), save it, and make it the ancestor of every User entity. THIS IS A PROBABLY A BAD IDEA since this limits your performance on User entities, particularly on writes. Every time a new user is created, all User entities are prevented from being updated.

I'm trying to illustrate how you need to think about transactions a bit differently in the HRD world. It's up to you to structure your data (using ancestors) so that you get good performance characteristics for your particular application. In fact, you might disagree with me and say that User entities will be updated so infrequently that it's ok to lock them all.

For illustrative purposes, another short-sighted possibility is to create multiple ancestors based on the username. ie, one for each letter of the alphabet. Then when you need to create a new User, you can search based on the appropriate ancestor. While this is an improvement from having a single ancestor (it's 26 times better), it still limits your future performance up front. This may be ok if you know right now the total number of users you will eventually have, but I suspect you want hundreds of millions of users.

The best way is to go back to the other suggestion and make the username the key. This allows you the best scalability, since getting/setting the User entity by key can be transactional and won't lock down other entities, limiting your scalability.

You'll need to find a way to work your application around this. For example, whatever information you get before the username can be stored in another entity that has a RelatedField to the User which is created later. Or you can copy that data into the User entity after the User entity is created by key, then remove the original entity.

查看更多
倾城 Initia
3楼-- · 2019-07-08 11:23

If usernames are unique why dont you make it the key?

class User(db.Model):
    @property
    def username(self):
        return self.key().name()
    ....

User.get_or_insert(username,field1=value1,....)

Note: You will not need transactions if you use get_or_insert

查看更多
登录 后发表回答