Also, if you wanted to use that same pattern of 2 transactions for
a delete, you should use a query for the delete to avoid conflict
with the cache.

    tx.begin();

    qRT = pm.newQuery(Event.class);
    qRT.setFilter(" (key == k) ");
    qRT.declareParameters("com.google.appengine.api.datastore.Key k");
    long ndeleted = qRT.deletePersistentAll(key);

    pm.flush();

    tx.commit();

On Jul 18, 8:49 pm, Nichole <[email protected]> wrote:
> By the way, if you wanted vary your jdoconfig.xml settings
> knowing which are impl in appengine,  here's a link to
> appengine datanucleus test code:
>
> http://code.google.com/p/datanucleus-appengine/source/browse/trunk/te...
>
> On Jul 18, 8:44 pm, Nichole <[email protected]> wrote:
>
>
>
>
>
>
>
> > The answer to the nontransactionalread and write are below, but
> > meanwhile, if you really do need those updates to be as close
> > to atomic as possible, then you need them in the same entity
> > group so they're co-located in the distributed file system.
>
> > On Jul 18, 2:02 am, mscwd01 <[email protected]> wrote:
>
> > > Thanks for the update. 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 the two updates to occur as
> > > quickly as possible so relying on the second update to 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 those two
> > > 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.
>
> > > >    Regarding update 1 being committed and update 2 failing, the
> > > > first is already committed, yes.  I think one has to use a retry for
> > > > the
> > > > 2nd update (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:
> > > >  *     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 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...
>
> 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