Best way to see if enity exist

1,485 views
Skip to first unread message

Michael

unread,
Jun 9, 2010, 8:51:29 PM6/9/10
to objectify-appengine
Hi,

I have a class that uses the username as the @Id field. I have a
section of code that checks to see if the username exists. What is
the best way to check this. The two ways I thought of is to query it
and check the result or do a get and catch the exception thrown if
there is no entity. Is there a better way, or which one of these would
be best?

Thanks
Michael

Jaroslav Záruba

unread,
Jun 9, 2010, 9:22:15 PM6/9/10
to objectify...@googlegroups.com
I'd recommend using find() rather then get, with find you don't have to catch anything.
(As for queries, they are generally slower then get/find, according to the docs.)

Cheers
  J. Záruba

Jeff Schnitzer

unread,
Jun 9, 2010, 11:29:12 PM6/9/10
to objectify...@googlegroups.com
I have a suspicion that what you want is a little more complicated
than just checking to see if an entity exists. You're trying to
guarantee that only one user can create an account with a particular
username, right?

This is actually more complicated than it sounds in appengine because
there are no unique indexes. The only way to enforce a unique
username is to use a transaction. The username must be the primary
key of the entity. The rough pseudocode is something like this:

start transaction
load entity with the username
if there is an entity
abort and return duplicate error
else
create the entity
commit the transaction
if the commit fails
abort and return duplicate error
else
everything is great!

Jeff

Jaroslav Záruba

unread,
Jun 9, 2010, 11:36:35 PM6/9/10
to objectify...@googlegroups.com
I have been under the impression that when you put an entity with key that already exists the old simply gets overwritten.
Is that false?

In other words: How could the transaction fail to commit only because of non-unique id/key?

Jaroslav Záruba

unread,
Jun 9, 2010, 11:45:52 PM6/9/10
to objectify...@googlegroups.com
Oh sorry, I guess the elimination of concurrency/overwriting is actually what you're after.
ignore previous message

2010/6/10 Jaroslav Záruba <jarosla...@gmail.com>

Jaroslav Záruba

unread,
Jun 9, 2010, 11:49:40 PM6/9/10
to objectify...@googlegroups.com
Nevertheless I still don't understand following:
How could the transaction fail to commit only because of non-unique id/key?

2010/6/10 Jaroslav Záruba <jarosla...@gmail.com>

Jeff Schnitzer

unread,
Jun 9, 2010, 11:58:39 PM6/9/10
to objectify...@googlegroups.com
GAE has a builtin optimistic concurrency management of transactions.
If someone committed a change to your entity before your commit, your
commit will fail.

There's a really good explanation of how it all works in Ryan Barett's
"Under The Covers Of The Google App Engine Datastore" talk:

http://sites.google.com/site/io/under-the-covers-of-the-google-app-engine-datastore

If you're going to watch one I/O session ever, this is the one to
watch. I've gone over it multiple times - it really really helps!

Jeff

2010/6/9 Jaroslav Záruba <jarosla...@gmail.com>:

Jaroslav Záruba

unread,
Jun 10, 2010, 12:09:56 AM6/10/10
to objectify...@googlegroups.com
On Thu, Jun 10, 2010 at 5:58 AM, Jeff Schnitzer <je...@infohazard.org> wrote:
GAE has a builtin optimistic concurrency management of transactions.
If someone committed a change to your entity before your commit, your
commit will fail.

That's what confuses me. How can Datastore tell I'm committing entity that has been modified meanwhile when I'm creating new one. Does it compare keys others have created since my txn.begin() when I call txn.commit()? Hopefully the video makes that clear for me. :)
 
There's a really good explanation of how it all works in Ryan Barett's
"Under The Covers Of The Google App Engine Datastore" talk:

http://sites.google.com/site/io/under-the-covers-of-the-google-app-engine-datastore

Yeah, that's the video where Ryan says we're not charged for indexes. ("Those were the days..." :))
Seems like I wasn't careful enough and should watch it once again. I'm on it!

Jaroslav Záruba

unread,
Jun 10, 2010, 12:40:34 AM6/10/10
to objectify...@googlegroups.com
2010/6/10 Jaroslav Záruba <jarosla...@gmail.com>

On Thu, Jun 10, 2010 at 5:58 AM, Jeff Schnitzer <je...@infohazard.org> wrote:
GAE has a builtin optimistic concurrency management of transactions.
If someone committed a change to your entity before your commit, your
commit will fail.

That's what confuses me. How can Datastore tell I'm committing entity that has been modified meanwhile when I'm creating new one.

ok, with identical key i'm actually updating not creating

Jeff Schnitzer

unread,
Jun 10, 2010, 1:13:43 AM6/10/10
to objectify...@googlegroups.com
2010/6/9 Jaroslav Záruba <jarosla...@gmail.com>:

>
> That's what confuses me. How can Datastore tell I'm committing entity that
> has been modified meanwhile when I'm creating new one. Does it compare keys
> others have created since my txn.begin() when I call txn.commit()? Hopefully
> the video makes that clear for me. :)

It will, but the very short version (as I vaguely remember it) is
this: Whenever an entity is written, the timestamp that lives at the
root of the entity group is updated. When a transaction starts, it
has a time... and when a commit is made, the transaction start time is
compared against the last update time. If the entity group timestamp
is newer than your transactions start time, you get rejected.

Something like that, at any rate. Crap, now I need to watch that
video yet again...

> Yeah, that's the video where Ryan says we're not charged for indexes.
> ("Those were the days..." :))
> Seems like I wasn't careful enough and should watch it once again. I'm on
> it!

:-)

Jeff

Jaroslav Záruba

unread,
Jun 10, 2010, 1:30:12 AM6/10/10
to objectify...@googlegroups.com
yeah, it already makes sense... i mean even to me :)

On Thu, Jun 10, 2010 at 7:13 AM, Jeff Schnitzer <je...@infohazard.org> wrote:
2010/6/9 Jaroslav Záruba <jarosla...@gmail.com>:
>
> That's what confuses me. How can Datastore tell I'm committing entity that
> has been modified meanwhile when I'm creating new one. Does it compare keys
> others have created since my txn.begin() when I call txn.commit()? Hopefully
> the video makes that clear for me. :)

It will, but the very short version (as I vaguely remember it) is
this:  Whenever an entity is written, the timestamp that lives at the
root of the entity group is updated.  When a transaction starts, it
has a time... and when a commit is made, the transaction start time is
compared against the last update time.  If the entity group timestamp
is newer than your transactions start time, you get rejected.

Something like that, at any rate.  Crap, now I need to watch that
video yet again...

David Fuelling

unread,
Jun 10, 2010, 10:59:05 AM6/10/10
to objectify-appengine
Another option would be to create your User class with a Long Id, and
have username as just a regular property. This way, your user will be
able to change their username without you have to do anything to other
data that's linked to a particular user (A good example of this is on
Twitter, where you can actually update your Twitter UserName without
losing all of your previous tweets).

Of course, with Google Appengine, you can't start a transaction across
two different entities, so you need to employ the UserNameLock pattern
that Jeff talked about in another thread. Here's his writeup that I'm
employing on my site to enforce username uniqueness:

http://groups.google.com/group/objectify-appengine/browse_thread/thread/25a9abfc86f8be51/88a5e7ca7be91cb4?lnk=gst&q=UserNameLock#88a5e7ca7be91cb4

david

aswath satrasala

unread,
Jun 16, 2010, 2:24:49 AM6/16/10
to objectify...@googlegroups.com
Hi,
In the 'else' part of the rough pseudo code, I am just wondering why would the commit fail.  It might fail other entitygroup contention or something else... I think it should not fail for a duplicate entity.

Datastore transactions are executed serially.  Please see the link
http://code.google.com/appengine/articles/transaction_isolation.html

-Aswath

On Thu, Jun 10, 2010 at 8:59 AM, Jeff Schnitzer <je...@infohazard.org> wrote
:

Jeff Schnitzer

unread,
Jun 16, 2010, 3:17:10 AM6/16/10
to objectify...@googlegroups.com
That is not correct. Commits to an entity group are executed in
serial, but transactions execute in parallel. Serializable
isolation level is enforced by optimistic concurrency control in the
entity group timestamp. There Is No Locking.

It is explained here:

http://sites.google.com/site/io/under-the-covers-of-the-google-app-engine-datastore
(you might want to look at the slides that he skipped, starting with #55)

The short version:

TransactionA reads Entity123
TransactionB writes Entity123
TransactionB commits
TransactionA writes Entity123
TransactionA commits - and fails because timestamp of Entity123 changed

Jeff

Reply all
Reply to author
Forward
0 new messages