The 2 properties that you are trying to set nontransactionalRead and
nontransactionalRead are what is set in the jdoconfig.xml.

They define whether that persistence manager will allow queries
that are not in a transaction.  The datanucleus documentation isn't
as readily available for jdo 2.3, so here's what the kodo docs say:

2.5.10. javax.jdo.option.NontransactionalRead
Property name: javax.jdo.option.NontransactionalRead
Configuration API: kodo.conf.JDOConfiguration.getNontransactionalRead
Resource adaptor config-property: NontransactionalRead
Default: true
Description: Whether the JDO runtime will allow you to read data
outside of a transaction.
See Section 7.2, “PersistenceManagerFactory Properties” in the JDO
Overview for details.

2.5.11. javax.jdo.option.NontransactionalWrite
Property name: javax.jdo.option.NontransactionalWrite
Configuration API: kodo.conf.JDOConfiguration.getNontransactionalWrite
Resource adaptor config-property: NontransactionalWrite
Default: false
Description: Whether you can modify persistent fields outside of a
transaction.
See Section 7.2, “PersistenceManagerFactory Properties” in the JDO
Overview for details.

http://download.oracle.com/docs/cd/E13189_01/kodo/docs316/ref_guide_conf_jdo.html#javax.jdo.option.NontransactionalRead

One last post of the example that includes asserts and a few
more comments to make it readable:

package com.climbwithyourfeet.software.twotransaction.model;

import com.climbwithyourfeet.software.twotransaction.util.PMF;
import com.google.appengine.api.datastore.Key;
import java.util.ArrayList;
import javax.jdo.Transaction;
import javax.jdo.PersistenceManager;
import java.util.logging.Logger;
import com.google.appengine.tools.development.LocalDatastoreTestCase;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

/**
   Goal:
 *     Update 2 entities which reside in 2 different entity groups.
 *     The updates must both pass or both fail.
 *
 * Solution:
 *     Use a different transaction for each entity,
 *      configurable in jdoconfig.xml.
 *     (Note that it looks like 2 named PersistenceMaangerFactory
 *      connections are
 *      possible, but not more than that, and there is only one
 *      transaction for a PMF connection).
 *
 *     Solution is essentially check that transaction1 succeeds
 *     or fails before committing transaction2.
 *
 *     The case which needs additional fail-over is the case in
 *     which transaction 1 is committed successfully but transaction2
 *     fails. In this case a retry of transaction2 should be invoked
 *     and must eventually succeed.
 *
 *     For that reason, any code using this pattern should design the
 *     logic so that the logic in the 2nd transaction can be
consistent
 *     on a longer timescale.
 *
 * @author nichole
 */
public class TwoOperationPseudoTransactionTest extends
    LocalDatastoreTestCase {

    private final Logger log = Logger.getLogger(
        this.getClass().getName());

    private String iden1 = "1234567";
    private String iden2 = "1123456";

    // change to for tests
    final boolean commit1 = true;
    final boolean commit2 = false;

    public TwoOperationPseudoTransactionTest() {
        super();
    }

    @Before
    public void setUp() throws Exception {
        super.setUp();

        log.info("setUp");

        try {

            PersistenceManager pm =
                PMF.get().getPersistenceManager();
            Transaction tx = pm.currentTransaction();
            tx.begin();
            UserGameCredits e1 = new UserGameCredits(iden1);
            pm.makePersistent(e1);
            pm.flush();
            tx.commit();

            PersistenceManager pm2 =
                PMF.get2().getPersistenceManager();
            tx = pm2.currentTransaction();
            tx.begin();
            UserAccount e2 = new UserAccount(iden2);
            pm2.makePersistent(e2);
            pm2.flush();
            tx.commit();
        } catch (Throwable t) {
            String msg = t.getMessage();
        }
    }

    @After
    public void tearDown() throws Exception {

        PersistenceManager pm = null;

        try {
            pm = PMF.get().getPersistenceManager();

            Key key1 = UserGameCredits.createKey(iden1);
            UserGameCredits e1 =
                pm.getObjectById(UserGameCredits.class, key1);
            String var1 = e1.getVariable();

            Key key2 = UserAccount.createKey(iden2);
            UserAccount e2 =
                pm.getObjectById(UserAccount.class, key2);
            String var2 = e2.getVariable();

            log.info("var1=" + var1);
            log.info("var2=" + var2);

            if (commit1 && commit2) {
                assertEquals(var1, "updated");
                assertEquals(var2, "updated");

            } else if (commit1 && !commit2) {
                assertEquals(var1, "updated");
                assertEquals(var2, "");
            } else {
                assertEquals(var1, iden1);
                assertEquals(var2, iden2);
            }
        } catch (Throwable t) {
            String msg = t.getMessage();
        } finally {
            if (pm != null) {
                pm.close();
            }
        }

        super.tearDown();
    }

    @Test
    public void testSimplest() throws Exception {

        PersistenceManager pm = PMF.get().getPersistenceManager();

        PersistenceManager pm2 = PMF.get2().getPersistenceManager();

        final Transaction tx = pm.currentTransaction();
        final Transaction tx2 = pm2.currentTransaction();

        final List<Boolean> completedOp1 = new ArrayList<Boolean>();
        final List<Boolean> completedOp2 = new ArrayList<Boolean>();

        try {

            tx.setSynchronization(new
javax.transaction.Synchronization() {
                public void beforeCompletion() {
                    log.info("before transaction 1");
                }
                public void afterCompletion(int status) {
                    switch (status) {
                        case
javax.transaction.Status.STATUS_MARKED_ROLLBACK :
                            // fall through
                        case
javax.transaction.Status.STATUS_ROLLEDBACK :
                            log.severe("rollback transaction 1");
                            break;
                        case
javax.transaction.Status.STATUS_COMMITTED:
                            log.info("committed transaction 1");
                            completedOp1.add(Boolean.TRUE);
                            break;
                        case javax.transaction.Status.STATUS_UNKNOWN:
                            // treat as rollback both?
                    }
                }
            });
            tx2.setSynchronization(new
javax.transaction.Synchronization() {
                public void beforeCompletion() {
                    log.info("before transaction 2");
                }
                public void afterCompletion(int status) {
                    switch (status) {
                        case
javax.transaction.Status.STATUS_MARKED_ROLLBACK :
                            // fall through
                        case
javax.transaction.Status.STATUS_ROLLEDBACK :
                            log.severe("rollback transaction 2");
                            if (!completedOp1.isEmpty() &&
completedOp1.get(0)) {
                                log.severe("1st transaction committed,
but 2nd did not");
                                //TODO this is the case we need to
apply application logic for
                                retry2ndOperation();
                            }
                            break;
                        case
javax.transaction.Status.STATUS_COMMITTED:
                            log.info("committed transaction 2");
                            completedOp2.add(Boolean.TRUE);
                            break;
                        case javax.transaction.Status.STATUS_UNKNOWN:
                            // treat as rollback both?
                    }
                }
            });

            tx.begin();

            Key key1 = UserGameCredits.createKey(iden1);
            UserGameCredits e1 =
pm.getObjectById(UserGameCredits.class, key1);
            e1.setVariable("updated");

            pm.flush();

            if (commit1) {
                tx.commit();
            } else {
                log.info("we will not commit transaction 1");
            }

            if (!completedOp1.isEmpty() && completedOp1.get(0)) {

                tx2.begin();

                Key key2 = UserAccount.createKey(iden2);
                UserAccount e2 = pm.getObjectById(UserAccount.class,
key2);
                e2.setVariable("updated");

                // NOTOE: this block shouldn't be in place for real
code!
                // it's to simulate error
                if (commit2) {
                    pm2.flush();
                    tx2.commit();
                }

            } else {
                log.info("we will not commit transaction 2");
            }

        } catch (Throwable t) {

            String msg = t.getMessage();

        } finally {

            if ((tx != null) && tx.isActive()) {
                if (tx.isActive())
                    tx.rollback();
            }
            if ((tx2 != null) && tx2.isActive()) {
                if (tx2.isActive())
                    tx2.rollback();
            }

            // NOTOE: this block shouldn't be in place for real code!
            // it's to simulate error
            if (!commit2) {
                tx2.rollback();
            }

            if (pm != null) {
                pm.close();
            }
            if (pm2 != null) {
                pm2.close();
            }
        }

    }

    private void retry2ndOperation() {
        //TODO this is the case we need to apply application logic for
        log.info("Create a task to retry the 2nd operation. This has
to succeed eventually.");
    }

}


On Jul 18, 1:09 pm, mscwd01 <[email protected]> wrote:
> Last question, promise!
>
> I decided to do what the above exception so kindly suggested and
> created a new PersistenceManager as follows:
>
>   PersistenceManagerFactory pmfInstance =
> JDOHelper.getPersistenceManagerFactory("transactions-optional");
>   pmfInstance.setNontransactionalRead(true);
>   pmfInstance.setNontransactionalWrite(true);
>
> Basically, I set both NontransactionalRead and NontransactionalWrite
> to true. This seemed to solve my issue and rolled back the first
> transaction if the second update failed. Great! However, I'm not sure
> what these two settings do and I'm slightly worried I may be getting
> myself into more problems by messing with them. Could someone kindly
> tell me if my "fix" is suitable and/or safe?
>
> Thanks
>
> On Jul 18, 10:02 am, mscwd01 <[email protected]> wrote:
>
>
>
>
>
>
>
> > Thanks for theupdate. It's a pity you cant check to see if the first
> > transaction committed before it leaves an "active" state, that'd make
> > things so much easier. I really needed thetwoupdates to occur as
> > quickly as possible so relying on the secondupdateto continue
> > retying via the task queue, is not ideal. Would running the
> > PersistenceManagerFactory with 'NontransactionalRead' and
> > 'NontransactionalWrite' set to 'true' allow me to do what I want? I
> > cant really find any documentation that describes what thosetwo
> > values do.
>
> > Thanks again
>
> > On Jul 17, 11:46 pm,Nichole<[email protected]> wrote:
>
> > > Good point, I rewrote the code below to better use the available
> > > connections
> > > and improve the pattern.
>
> > >    Regardingupdate1 being committed andupdate2 failing, the
> > > first is already committed, yes.  I think one has to use a retry for
> > > the
> > > 2ndupdate(using the task queue structure) for the 2nd operation to
> > > eventually succeed, but on a longer timescale.
>
> > > Here's a better approach to the problem:
>
> > > import com.climbwithyourfeet.software.twotransaction.util.PMF;
> > > import com.google.appengine.api.datastore.Key;
> > > import java.util.ArrayList;
> > > import javax.jdo.Transaction;
> > > import javax.jdo.PersistenceManager;
> > > import java.util.logging.Logger;
> > > import com.google.appengine.tools.development.LocalDatastoreTestCase;
> > > import java.util.List;
> > > import org.junit.After;
> > > import org.junit.Before;
> > > import org.junit.Test;
>
> > > /**
> > >    Goal:
> > >  *    Update2entitieswhich reside in 2 different entity groups.
> > >  *     The updates must both pass or both fail.
> > >  *
> > >  * Solution:
> > >  *     Use a different transaction for each entity, configurable in
> > >         jdoconfig.xml.
> > >  *     (Note that it looks like 2 named PersistenceManagerFactory
> > >         connections are possible, but not more than that, and there
> > >         is only one transaction for a  PMF connection).
> > >  *
> > >  *     Solution is essentially check that transaction1 succeeds or
> > > fails before
> > >  *     committing transaction2.
> > >  *
> > >  *     The case which needs additional fail-over is the case in which
> > >        transaction 1 is committed successfully but transaction2 fails.
> > >  *     In this case a retry of transaction2 should be invoked and must
> > >        eventually succeed.
> > >  *
> > >  *     For that reason, any code using this pattern should design
> > >        the logic so that the logic in the 2nd transaction can be
> > > consistent
> > >        on a longer timescale.
> > >  *
> > >  * @authornichole
> > >  */
> > > public class TwoOperationPseudoTransactionTest extends
> > > LocalDatastoreTestCase {
>
> > >     private final Logger log =
> > > Logger.getLogger(this.getClass().getName());
> > >     private String iden1 = "1234567";
> > >     private String iden2 = "1123456";
>
> > >     public TwoOperationPseudoTransactionTest() {
> > >         super();
> > >     }
>
> > >     @Before
> > >     public void setUp() throws Exception {
> > >         super.setUp();
>
> > >         try {
> > >             PersistenceManager pm = PMF.get().getPersistenceManager();
> > >             Transaction tx = pm.currentTransaction();
> > >             tx.begin();
> > >             UserGameCredits e1 = new UserGameCredits(iden1);
> > >             pm.makePersistent(e1);
> > >             pm.flush();
> > >             tx.commit();
>
> > >             PersistenceManager pm2 =
> > > PMF.get2().getPersistenceManager();
> > >             tx = pm2.currentTransaction();
> > >             tx.begin();
> > >             UserAccount e2 = new UserAccount(iden2);
> > >             pm2.makePersistent(e2);
> > >             pm2.flush();
> > >             tx.commit();
> > >         } catch (Throwable t) {
> > >             String msg = t.getMessage();
> > >         }
> > >     }
>
> > >     @After
> > >     public void tearDown() throws Exception {
> > >         super.tearDown();
> > >     }
>
> > >     @Test
> > >     public void test2() throws Exception {
>
> > >         PersistenceManager pm = PMF.get().getPersistenceManager();
> > >         PersistenceManager pm2 = PMF.get2().getPersistenceManager();
>
> > >         final Transaction tx = pm.currentTransaction();
> > >         final Transaction tx2 = pm2.currentTransaction();
>
> > >         final List<Boolean> completedOp1 = new ArrayList<Boolean>();
> > >         final List<Boolean> completedOp2 = new ArrayList<Boolean>();
>
> > >         try {
>
> > >             // change to for tests
> > >             final boolean commit1 = true;
> > >             final boolean commit2 = false;
>
> > >             tx.setSynchronization(new
> > > javax.transaction.Synchronization() {
> > >                 public void beforeCompletion() {
> > >                     log.info("before transaction 1");
> > >                 }
> > >                 public void afterCompletion(int status) {
> > >                     switch (status) {
> > >                         case
> > > javax.transaction.Status.STATUS_MARKED_ROLLBACK :
> > >                             // fall through
> > >                         case
> > > javax.transaction.Status.STATUS_ROLLEDBACK :
> > >                             log.severe("rollback transaction 1");
> > >                             break;
> > >                         case
> > > javax.transaction.Status.STATUS_COMMITTED:
> > >                             log.info("committed transaction 1");
> > >                             completedOp1.add(Boolean.TRUE);
> > >                             break;
> > >                         case javax.transaction.Status.STATUS_UNKNOWN:
> > >                             // treat as rollback both?
> > >                     }
> > >                 }
> > >             });
> > >             tx2.setSynchronization(new
> > > javax.transaction.Synchronization() {
> > >                 public void beforeCompletion() {
> > >                     log.info("before transaction 2");
> > >                 }
> > >                 public void afterCompletion(int status) {
>
> > >                     switch (status) {
> > >                         case
> > > javax.transaction.Status.STATUS_MARKED_ROLLBACK :
> > >                             // fall through
> > >                         case
> > > javax.transaction.Status.STATUS_ROLLEDBACK :
> > >                             log.severe("rollback transaction 2");
> > >                             if (!completedOp1.isEmpty() &&
> > > completedOp1.get(0)) {
> > >                                 log.severe("1st transaction committed,
> > > but 2nd did not");
> > >                                 //TODO this is the case we need to
> > > apply application logic for
> > >                                 retry2ndOperation();
> > >                             }
> > >                             break;
> > >                         case
> > > javax.transaction.Status.STATUS_COMMITTED:
> > >                             log.info("committed transaction 2");
> > >                             completedOp2.add(Boolean.TRUE);
> > >                             break;
> > >                         case javax.transaction.Status.STATUS_UNKNOWN:
> > >                             // treat as rollback both?
> > >                     }
> > >                 }
> > >             });
>
> > >             tx.begin();
>
> > >             Key key1 = UserGameCredits.createKey(iden1);
> > >             UserGameCredits e1 =
> > > pm.getObjectById(UserGameCredits.class, key1);
> > >             e1.setVariable("updated");
>
> > >             pm.flush();
>
> > >             if (commit1) {
> > >                 tx.commit();
> > >             }
>
> > >             if (!completedOp1.isEmpty() && completedOp1.get(0)) {
>
> > >                 tx2.begin();
>
> > >                 Key key2 = UserAccount.createKey(iden2);
> > >                 UserAccount e2 = pm.getObjectById(UserAccount.class,
> > > key2);
> > >                 e2.setVariable("updated");
>
> > >                 pm2.flush();
>
> > >                 // test structure that shouldn't be in place for real
> > > code!
> > >                 // (the retry is in the sync code above)
> > >                 if (commit2) {
> > >                     tx2.commit();
> > >                 } else {
>
> ...
>
> read more »

-- 
You received this message because you are subscribed to the Google Groups 
"Google App Engine for Java" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/google-appengine-java?hl=en.

Reply via email to