The datastore documentation says that:
Upon commiting a transaction for the entity groups, App Engine again checks the last update time for the entity groups used in the transaction. If it has changed since our initial check, App Engine throws an exception
I have three test cases about performing a transacted datastore put of a new Entity("TestEntity",1) in two concurrent transactions.
test1: put the entity in the first transaction, put the entity in the second transaction, and commit both transactions. This test passes (i.e. throws
ConcurrentModificationException) when run in developement server and standalone unit tests, but fails (i.e. executes without throwing an exception) when run in production server.test2: put the entity in the first transaction and commit it, then put the entity in the second transaction and commit it. This test always fails.
test3: attempt to get the (non-existing) entity in both transactions, then do test2. This test always passes by throwing
ConcurrentModificationException.
From these tests I conclude that neither beginTransaction nor put guarantee that "the initial check" is performed, and I need to pay a get in order to guarantee the integrity of the transaction. Is that correct?
@Test(expected=ConcurrentModificationException.class)
//put1 put2 commit1 commit2
public void test1() {
DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
Transaction txn1 = ds.beginTransaction();
Transaction txn2 = ds.beginTransaction();
ds.put(txn1,new Entity("TestEntity",1));
ds.put(txn2,new Entity("TestEntity",1));
txn1.commit();
txn2.commit();
}
@Test(expected=ConcurrentModificationException.class)
//put1 commit1 put2 commit2
public void test2() {
DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
Transaction txn1 = ds.beginTransaction();
Transaction txn2 = ds.beginTransaction();
ds.put(txn1,new Entity("TestEntity",1));
txn1.commit();
ds.put(txn2,new Entity("TestEntity",1));
txn2.commit();
}
@Test(expected=ConcurrentModificationException.class)
//get1 get2 put1 commit1 put2 commit2
public void test3() throws InterruptedException, ExecutionException {
DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
ds.delete(KeyFactory.createKey("TestEntity", 1));
Transaction txn1 = ds.beginTransaction();
Transaction txn2 = ds.beginTransaction();
Assert.assertNull(getTestEntity(ds, txn1));
Assert.assertNull(getTestEntity(ds, txn2));
ds.put(txn1,new Entity("TestEntity",1));
txn1.commit();
ds.put(txn2,new Entity("TestEntity",1));
txn2.commit();
}