0

I have an OfyService class of this type

/**
 * Custom Objectify Service that this application should use.
 */
public class OfyService {
    /**
     * This static block ensure the entity registration.
     */
    static {
        factory().register(MerchantProfile.class);
        factory().register(Product.class); 
    }

    /**
     * Use this static method for getting the Objectify service object in order to make sure the
     * above static block is executed before using Objectify.
     * @return Objectify service object.
     */
    public static Objectify ofy() {
        return ObjectifyService.ofy();
    }

    /**
     * Use this static method for getting the Objectify service factory.
     * @return ObjectifyFactory.
     */
    public static ObjectifyFactory factory() {
        return ObjectifyService.factory();
    }
}

I use factory().allocateId() method to allocate Key (to get Long id) before saving an entity. I have a problem where I need to transfer money from one account to the other and add an entry to Transaction table. So, I use ofy().transact(new Work<~>) in the following way

WrappedBoolean result = ofy().transact(new Work<WrappedBoolean>() {
            @Override
            public WrappedBoolean run() {


            }
}

I allocate Id for Transaction before entering the transact part and then I subtract money from one account add it to other and then save both the accounts and Transaction entity.

My concern is as follows

  1. What happens when there are two concurrent requests and app engine Instance provide them separate request handlers and same ID is allocated to both of them, depending upon the database State or it is not possible that the same id gets allocated twice.
  2. What is the flow of control of Work as compared to the conventional synchronization block that we use in Java for making critical sections?

PS: To perform the same in other frameworks like Jersey (with JPA) I would have used a Synchronization block and would have done the Transaction in that block. And since at a time only one thread can access that block and id is also assigned once data is saved to the table there would have bee no issues.

1 Answers1

1

Thread safety is not relevant to data consistency with either the datastore or with JPA/RDBMSes. If you are relying on synchronization, you are doing something wrong.

If you create a complete unit of work that performs your task and execute it in a transaction, the datastore will ensure that it is either completely applied or not applied at all. It will also guarantee that all transactions behave as if they were operated in serial. This might result in any particular execution aborting and retrying, but you don't see this as a user.

In short: Just put this in a transaction and do not worry about threading.

stickfigure
  • 13,458
  • 5
  • 34
  • 50
  • Thanks for the insight. Also, should I allocateId before entering into ofy().transact or it should be done inside run method. So that in case of any failure Objectify will allocate different id for saving transaction entity to the datastore. Since if I do it before entering into ofy().transact there might be a case where two separate entity (of same type Transaction) will get same id allocated to them running in different request handlers and there might be an exception in run method. Or Objectify will handle all such cases. Please correct me if I am wrong in this. – Utkarsh Saxena Jun 22 '17 at 12:11
  • Also, how are the threads maintained when ofy().transact is called somewhere in the code while running on Google endpoints framework (Java). – Utkarsh Saxena Jun 22 '17 at 12:16
  • allocateId() will never return the same number twice under any circumstances. That said, it's a good practice to allocate ids outside of transactions because it guarantees that each retry does the exact same work; if you make your transactions fully idempotent you can even retry timeouts (but that's a whole different story). I don't understand your question about threads. When your request handling code starts executing you only have one thread, the request thread. – stickfigure Jun 22 '17 at 15:58
  • allocateId() sounds very cool. I was not really very sure about this but now I am, thanks to you. About the threads, I was somewhat mixing both the concepts but now I am clear about it. Thanks !! – Utkarsh Saxena Jun 22 '17 at 17:16
  • I personally prefer to never use the allocateID method, and instead create my own keys which will always be unique to the data. You could make the key be anything you want really. What about account number + "," current balance + "," + ending balance +"," + getTimeInMs()? – KevinG Jun 23 '17 at 00:15