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.
