Question

The Appengine docs say this about Transactions in the Datastore: http://code.google.com/appengine/docs/java/datastore/transactions.html#Isolation_and_Consistency

In a transaction, all reads reflect the current, consistent state of the 
Datastore at the time the transaction started. This does not include
previous puts and deletes inside the transaction. Queries and gets inside
a transaction are guaranteed to see a single, consistent snapshot of the
Datastore as of the beginning of the transaction.

With that in mind, I have created the following two unit tests to test this (against the local datastore). I would expecte both of my tests below to pass. However, only "test1" passes, whereas "test2" fails. The only difference is a commit of tx1 in "test1".

Is this a bug in the local datastore, a misunderstanding of the GAE docs, or a bug in my unit tests? Or something else?

Thanks!

@Test(expected = EntityNotFoundException.class)
public void test1() throws EntityNotFoundException {
    DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

    // Create 2 Transactions...
    Transaction txn1 = datastore.beginTransaction();
    Transaction txn2 = datastore.beginTransaction();

    try {
        Key entityWithStringKey = KeyFactory.createKey("TestEntity", "test");
        Entity entityWithString = new Entity(entityWithStringKey);
        datastore.put(txn1, entityWithString);

        entityWithString = datastore.get(txn2, entityWithStringKey);
        // The above should throw EntityNotFoundException
        assertNull(entityWithString);
    }
    finally {
        if (txn1.isActive()) {
        txn1.rollback();
    }

    if (txn2.isActive()) {
            txn2.rollback();
    }
}


@Test(expected = EntityNotFoundException.class)
public void test2() throws EntityNotFoundException {
    DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

    // Create 2 Transactions...
    Transaction txn1 = datastore.beginTransaction();
    Transaction txn2 = datastore.beginTransaction();

    Key entityWithStringKey = KeyFactory.createKey("TestEntity", "test");

    try {
        Entity entityWithString = new Entity(entityWithStringKey);
        datastore.put(txn1, entityWithString);
        txn1.commit();
    } finally {

    if (txn1.isActive()) {
        txn1.rollback();
    }
    }

    try {
        Entity entityWithString = datastore.get(txn2, entityWithStringKey);
        assertNull(entityWithString);
        // The above should throw EntityNotFoundException
    } 
    finally {
        if (txn2.isActive()) {
            txn2.rollback();
        }
    }
}
Was it helpful?

Solution

I suspect the transaction doesn't actually begin when when you call datastore.beginTransaction - it begins when the transaction first hits the database - that would be the optimal way to minimize locks on the database side.

In test 2, you can try putting in an additional get() on txn2, before the txn1.commit(). Then a second get() (where you currently do the txn2 get) should return null.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top