weird/interesting "multiple entity groups" case

13 views
Skip to first unread message

Jay Freeman (saurik)

unread,
Nov 13, 2008, 6:21:06 AM11/13/08
to google-a...@googlegroups.com
I am  running into a confusing "can't operate on multiple entity groups in a single transaction" case when I am, in fact, not accessing "multiple entity groups". I think I understand what might be going on, but A) am not certain and B) others might find it interesting, anyway.
 
What is happening is that I have a transaction that is operating over two models: GoogleUser and Account. I get the GoogleUser instance associated with the current e-mail address with get_by_key_name and then return gooser.account. If I fail to find that GoogleUser, I add a new Account, and into the /same entity group/ I add the GoogleUser in question.
 
For reference, here is the actual code of my transaction:
 
    132     @staticmethod
    133     def goi_account_by_primary_key_(user, hash, **kw):
    134         gooser = GoogleUser.get_by_key_name(hash)
    135         if gooser:
    136             account = db.get(gooser.account)
    137             keys = kw.keys()
    138             for key in keys:
    139                 setattr(account, key, kw[key])
    140             account.put()
    141         else:
    142             account = Account(**kw)
    143             account.put()
    144             gooser = GoogleUser(parent=account, key_name=hash, user=user, account=account)
    145             gooser.put()
    146         return account
Note that moving the creation of the gooser above the creation of the account (and swapping the parent/child relationship between them and making other required code changes to temporarily support that) does work.
 
Why does this happen?
 
Currently my two theories are: A) the detection code is incorrect, and is making assumptions about "same model", and B) entities that don't exist yet are only considered in the same entity group as that entity itself, so even though I'm going to create that entity later into the same entity group, I can't put a different object first as the data store can't tell the difference. I am guessing that B is correct, in which case maybe someone else working on this same problem will find this post and be helped by it. If it isn't B, then maybe someone would be kind enough to tell me what the problem actually is ;P.
 
-J

Charles Srisuwananukorn

unread,
Nov 13, 2008, 12:38:55 PM11/13/08
to google-a...@googlegroups.com
I was seeing this in my app too. The get_by_key_name on line 134 is actually getting a different GoogleUser than the one put in on line 144.

The full key of an entity includes the key names for each of its ancestors back to some root entity, meaning two entities of the same kind can have the same key name if they have different parents. From the docs:

The keys of two different entities can have similar parts as long as at least one part is different. For instance, two entities can have the same kind and name if they have different parents. (http://code.google.com/appengine/docs/datastore/keysandentitygroups.html)

So the get_by_key_name below is fetching a GoogleUser whose key name matches the value of "hash" and has no parents. To get the right GoogleUser, you'd need to use

GoogleUser.get(Key.from_path('Account', <account_id | account_key_name>, 'GoogleUser', hash))

where you specify either the ID or the key name of the account as well.

Hope that helps.

--Charles

Jay Freeman (saurik)

unread,
Nov 13, 2008, 4:51:32 PM11/13/08
to google-a...@googlegroups.com
WHAT?!? Frowny /pants/. The whole point of creating this model is that I don't have any way to actually look up the Account by key (you will note that I create it without passing any specific key_name/id arguments, although I guess I could have been sneaking them in through **kw). Specifically, I was trying to do something intelligent to get around the "user accounts system is next to useless, long term" problem brought up in the thread "Permanent Unique User Identifier".
 
Regardless, thanks. This was incredibly helpful. I think I'm just going to give up on Google's user authentication API and code up something with OpenID. The Google user authentication thing doesn't even work correctly on an iPhone anyway (it breaks horribly if you put in an incorrect password, and it always says New Service instead of your application name), and it doesn't even support logging out, so it was kind of stupid for me to be trying to rely on it anyway.
 
-J

djidjadji

unread,
Nov 28, 2008, 8:06:49 PM11/28/08
to google-a...@googlegroups.com
Why don't you make 2 transaction functions.
By taking line 134 and 135 outside the transaction.
One transaction function will be line 136..140 the other line 142..145.
You don't have the problem of having two GoogleUsers/Accounts in the
same tranaction.

Why don't you replace line 136 with
> 136 account = gooser.parent()
and drop the account attribute of GoogleUsers


2008/11/13 Jay Freeman (saurik) <sau...@saurik.com>:
Reply all
Reply to author
Forward
0 new messages