HRJ HRJ - 2 days ago 5
Java Question

When is the ConcurrentModificationException thrown in GAE?

I am reading the official GAE documentation on transactions and I can't understand when a

ConcurrentModificationException
is thrown.

Look at one of the examples which I am copy-pasting here:

int retries = 3;
while (true) {
Transaction txn = datastore.beginTransaction();
try {
Key boardKey = KeyFactory.createKey("MessageBoard", boardName);
Entity messageBoard = datastore.get(boardKey);

long count = (Long) messageBoard.getProperty("count");
++count;
messageBoard.setProperty("count", count);
datastore.put(messageBoard);

txn.commit();
break;
} catch (ConcurrentModificationException e) {
if (retries == 0) {
throw e;
}
// Allow retry to occur
--retries;
} finally {
if (txn.isActive()) {
txn.rollback();
}
}
}


Now, all the writes to the datastore (in this example) are wrapped under a transaction. So why would a
ConcurrentModificationException
be thrown?

Does it happen when some other code which is not wrapped in a transaction updates the same entity that is being modified by the above code? If I ensure that all code that updates an Entity is always wrapped in a transaction, is it guaranteed that I won't get a
ConcurrentModificationException
?

HRJ HRJ
Answer

I found the answer on the GAE mailing list.

I had a misconceived notion of how transactions work in GAE. I had imagined that beginning a transaction will lock out any concurrent updates to the datastore until the transaction commits. That would have been a performance nightmare as all updates would block on this transaction and I am happy that this isn't the case.

Instead, what happens is, the first update wins, and if a collision is detected in subsequent updates, then an exception is thrown.

This surprised me at first, because it means many transactions will need a retry logic. But it seems similar to the PostgreSQL semantics for "serializable isolation" level, though in PostgreSQL you also have the option to lock individual rows and columns.

Comments