I found the answer on the GAE mailing list.
I had a misconception about how transactions work in GAE. I assumed that the start of the transaction blocks any simultaneous updates to the data store until the transaction is complete. This would be a performance nightmare, since all updates would block this transaction, and I am glad that it is not.
Instead, the first update wins, and if a collision is detected in subsequent updates, an exception is thrown.
At first it surprised me because it means that many transactions will require retry logic. But it is similar to the PostgreSQL semantics for the “serialized isolation” layer, although in PostgreSQL you also have the option to block individual rows and columns.
source share