0

I am using GAE SDK 1.8.9 with Java 1.7 (build 45). For the datastore, I am using Datanucleus JDO v2. I have deployed code to production to update a counter for each new "order" request and am having a couple of consistency issues occurring that I believe are related to the processing of the requests from different servers within a few seconds of each other. The datastore entity consists of a unique key (generated by my app), a couple of date fields, and an orderId counter. There are only a few of these entities in the datastore.

Here is a subset of my logic...

keyId is an instance variable of type String within a StoreWrapper class

The newOrderId() method within the StoreWrapper class should fetch the desired Store entity (based on the keyId) within the datastore, increment the entity's orderId value, update the entity back to the datastore with the updated value, and finally return the updated value (or 0 if an error occurs) to the caller. The fetching, incrementing, and updating all occur within a JDO transaction using the .begin() and .commit() method for a transaction.

The newOrderId() method within the Store class simply increments the orderId value (plus some logic to allow it to rollover once it reaches a ceiling).

public int newOrderId() { int orderId = 0;

PersistenceManager pm = PMF.get().getPersistenceManager();
Transaction tx = pm.currentTransaction();

Store tempStore = null;
try {
    tx.begin(); // Use a transaction to modify value (read, update, write)
    tempStore = pm.getObjectById(Store.class, keyId);
    tempStore.newOrderId();
    pm.makePersistent(tempStore); // Problem occurs regardless if this statement exists or not
    tx.commit();
    orderId = tempStore.getOrderId();
} catch (JDOObjectNotFoundException e) {
    // Do new store stuff
    ...
} catch (JDOUserException e) {
    System.out.println(e.getMessage());
} catch (JDODataStoreException e) {
    System.out.println(e.getMessage());
} catch (JDOCanRetryException e) {
    System.out.println(e.getMessage());
} catch (JDOOptimisticVerificationException e) {
    System.out.println(e.getMessage());
} catch (JDOFatalException e) {
    System.out.println(e.getMessage());
} catch (JDOException e) {
    System.out.println(e.getMessage());
} catch (Exception e) {
    System.out.println(e.getMessage());
} finally {
    if (tx.isActive())
        tx.rollback();
    pm.close();
}

return orderId;

}

When I submit several requests to place an order over a short period, I notice duplicate order numbers in another datastore for the orders. My research has concluded this occurs when multiple instances are processing the requests. In the logs, I can see that each time this occurs, the instance is different for the duplicates. It is my understanding that the entity group defaults to the root (which would be the case for this situation given the simple nature of the entity since I don't specify a parent when generating the key), but nowhere can I find a clear understanding as to why the behavior is acting like "eventual consistency" rather than "strong consistency". I've placed several catches for the various exceptions, but see no evidence that any of these are occurring in the logs. I've even set the threadsafe to 'false' so only one request is processed per instance and it still occurs. Is there some setting that must be set to enforce "strong consistency" across the multiple instances?

  • When you say "simply increments the orderId value", what do you mean? Where is this value being stored? – Greg May 22 '14 at 20:29
  • If orderid is not persisted in a service like the datastore or memcache, then it will be independently incremented by each instance (in instance memory) servicing the request and will result in multiple entities with the same orderid. Remember each request can be served by completely independent instances, the only thing shared is memcache and the datastore (or other shared services like GCS to be accurate). – Tim Hoffman May 23 '14 at 01:20
  • The Store class represents the datastore entity and contains the orderId integer and a method newOrderId() to increment it. The method newOrderId() (same name) within the StoreWrapper class fetches the datastore entity via getObjectById(), then invokes the store.newOrderId() method to increment it, all within a transaction. This should persist the change to the orderId to the datastore and my understanding is that if another instance is doing this very same thing at the same time, one of them fails and throws an exception due to use of a transaction to enforce a "strong consistency" policy. – Joe Shobe May 23 '14 at 14:20

0 Answers0