Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 366
HRJ Avatar asked Jan 03 '12 20:01

HRJ


People also ask

What does the ConcurrentModificationException occur?

The ConcurrentModificationException generally occurs when working with Java Collections. The Collection classes in Java are very fail-fast and if they are attempted to be modified while a thread is iterating over it, a ConcurrentModificationException is thrown.

Who throws ConcurrentModificationException?

Class ConcurrentModificationException. This exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible. For example, it is not generally permissible for one thread to modify a Collection while another thread is iterating over it.

Does stream throw ConcurrentModificationException?

The iterators returned by all of this class's "collection view methods" are fail-fast: if the map is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove method, the iterator will throw a ConcurrentModificationException .

Does Hashmap throw ConcurrentModificationException?

This message says that the exception is thrown when the next method is called as the iterator is iterating the list and we are making modifications in it simultaneously. But if we make modifications in hashmap like given below, then it will not throw any such exception as the size of the hashmap won't change.


1 Answers

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.

like image 193
HRJ Avatar answered Oct 04 '22 08:10

HRJ