Search code examples
google-app-engineobjectify

Will two entities get written to datastore with this endpoint?


I have an endpoint method that first uses a query to see if an entity with certain params exists, and if it does not it will create it. If it exists, I want to increase a counter in the variable:

Report report = ofy().load().type(Report.class)
    .filter("userID", userID)
    .filter("state", state).first().now();

if (report == null) { 
    //write new entity to datastore with userID and state
} else {
    //increase counter in entity +1
    report.setCount(report.count+1)
    //save entity to datastore
}

My question is, what if someone clicks a button to execute the above endpoint with the same params very rapidly, what will happen? Will two identical Report entities get written to the datastore? I only want to make sure one is written.


Solution

  • By itself this code is not safe and has a race condition that will allow multiple Reports to be created.

    To make this safe, you need to run the code in a transaction. Which means you must have an ancestor query (or convert it to a simple primary key lookup). One option is to give Report a @Parent of the User. Then you can so something like this:

    ofy().transact(() -> {
        Report report = ofy().load().type(Report.class)
            .ancestor(user)
            .filter("state", state).first().now();
    
        if (report == null) { 
            //write new entity to datastore with userID and state
        } else {
            //increase counter in entity +1
            report.setCount(report.count+1)
            //save entity to datastore
        }
    });