How do I structure storage entities for a multiple account application?

27 views
Skip to first unread message

Phinaes Gage

unread,
Mar 20, 2012, 1:46:54 PM3/20/12
to google-a...@googlegroups.com
I'm working on a multi-user application where I'm using my own authentication / account scheme, but am running into trouble structuring accounts in GAE storage. Here's where I started and got tripped up:

- I'd like each user be a root level entity, and have all of the entities for that user be child entities, since transactions are almost always done on a single user.

- I'd like the username for users to be their email address, which they can change at any time, but each user should have a system-assigned integer ID that never changes.

Given this, I started creating Account entities at the root level, each with an "email" property containing their email address. My first problem came immediately, when trying to create an Account as follows:

public boolean createAccount(final Account ac) {
   
final DatastoreService ds = DatastoreServiceFactory.getDatastoreService();

    final Transaction txn = ds.beginTransaction();
   
try {
       
// see if an account with the same email already exists
       
final Query q = new Query("Account");
        q
.addFilter("email", Query.FilterOperator.EQUAL, ac.getEmail());

       
final PreparedQuery pq = ds.prepare(txn, q);
       
final boolean bCreated;

       
if (0 < pq.countEntities(FetchOptions.Builder.withLimit(1))) {
            bCreated
= false;
       
} else {
            ds
.put(EntityFactory.create(ac)); // EntityFactory just creates an entity of kind "Account" with the right fields set
            bCreated
= true;
       
}

    txn
.commit();
   
return bCreated;
   
} finally {
       
if (txn.isActive())
            txn
.rollback();
   
}
}


The problem is, I can't query and update root level entities in a transaction, because that query doesn't contain an ancestor query, so there's no way for me to atomically check that an Account with a given email address doesn't exist, then create the account. I get "java.lang.IllegalArgumentException: Only ancestor queries are allowed inside transactions.". How do I solve this, given the following restrictions?

- I don't want the email address to be the key for the entity, because then a user's email address can't change without copying their entire entity tree.

- I don't want users to have a separate username from their email address that's used as the entity key, as they tend to forget usernames, so I find their email address to be convenient.

- I don't want to create a dummy parent entity that all Accounts are under, because that would force all users to be in the same entity group, which would make transaction concurrency poor.

Any thoughts?
Reply all
Reply to author
Forward
0 new messages