David Buhler
David Buhler

Reputation: 25

How to Prevent Duplicate Values in Google App Engine / an Eventually Consistent Datastore During Authentication

We have an authentication system which uses Google App Engine as the backend. The underlying languages and frameworks consist of Java, JDO, Google Authentication & Google App Engine.

We have a User Entity. A User Entity is User Meta-data saved from the Google App Engine authentication response (for example: first name, last name, email address).

When a user tries to register or login (both a sign-in event), we scan the datastore to see if a user with the same email address already exists. If the user does not already exist, we create a new user. If the user does exist, we update and retrieve their user information and then retrieve their application data.

We need to run separate, multiple transactions, because the Login page is completely separate of the application page.

In some cases, when a user tries to register twice in a short period of time, a duplicate user is created. While we could always fetch the last user registered, we really want the query to be a unique JDO query. Using a unique JDO query, the JDO query (run inside a transaction) sometimes fails due to two identical values being found (for the user's email address).

We considered the following ways to enforce the constraint:

  1. storing a freshly registered user's information in cache. We didn't seem to find any information that would suggest cache would be a suitable solution to prevent duplicates. Is cache instantly available across machines? Even if it is, the cache could eject an entity for various reasons.
  2. storing the user's email in a session. This seemed to bloat the code and seemed a bizarre way to enforce constraints.

Is there a best-practices approach with Google App Engine to prevent duplicate values during an authentication workflow, and separate transactions?

Upvotes: 2

Views: 517

Answers (1)

Andrei Volgin
Andrei Volgin

Reputation: 41099

I would recommend a combination of these two approaches:

  1. Create a Login entity, which consists of a user's email and id of a main User entity. Make user's email a key in Login entity. This ensures its uniqueness.

The added bonus is that you can link more than one email address to the same User entity. (We have this option as a feature in our app).

Another added bonus: you can replace your login query with a get() call as you can create a key from the email address. This is cheaper, faster, and avoids the eventual consistency issues (get calls are always consistent).

  1. Store email address (or even a simple "Login" flag) in sessions. It's only 3-5 lines of code to add it and to check it on each login attempt, so I would not call it a "bloat".

In my apps I add a flag to sessions and check for this flag on each call to App Engine instances (not only login calls, but all calls for additional security), and then invalidate a session when a user logs out.

Upvotes: 4

Related Questions