Created
August 12, 2011 07:02
-
-
Save nking/1141609 to your computer and use it in GitHub Desktop.
Two operation updates in Google's Appengine using two, 2-phase locking transactions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The goal is to insert a new event and update the counter for it and have both | |
operations fail or both succeed. The solution below places each of these | |
2 operations is in a 2 two-phase locking transaction which must attempt to | |
both succeed or fail, and so the pattern of | |
lock1 operation1 (commit1 and lock2 operation2 commit2) or (rollback1) | |
is applied, with an additional pattern of first operation successfully committed | |
with the second retried if it fails. | |
private Key insert(Event event) { | |
PersistenceManager pmRT = null; | |
PersistenceManager pm = null; | |
Transaction txRT = null; | |
Transaction tx = null; | |
final List<Boolean> insertSucceeded = new ArrayList<Boolean>(); | |
final List<Boolean> counterUpdateSucceeded = new ArrayList<Boolean>(); | |
try { | |
pmRT = TransactionsRequiredPMF.get().getPersistenceManager(); | |
pm = PMF.get().getPersistenceManager(); | |
txRT = pmRT.currentTransaction(); | |
tx = pm.currentTransaction(); | |
txRT.setSynchronization(new javax.transaction.Synchronization() { | |
public void beforeCompletion() { | |
} | |
public void afterCompletion(int status) { | |
switch (status) { | |
case javax.transaction.Status.STATUS_ROLLEDBACK : | |
break; | |
case javax.transaction.Status.STATUS_COMMITTED: | |
log.info("insert succeeded"); | |
insertSucceeded.add(Boolean.TRUE); | |
break; | |
} | |
} | |
}); | |
tx.setSynchronization(new javax.transaction.Synchronization() { | |
public void beforeCompletion() { | |
} | |
public void afterCompletion(int status) { | |
switch (status) { | |
case javax.transaction.Status.STATUS_ROLLEDBACK : | |
if (!insertSucceeded.isEmpty() && insertSucceeded.get(0)) { | |
log.severe("insert succeeded, but update did not"); | |
startIncrementEventAndVenueCountersTasks(); | |
} | |
break; | |
case javax.transaction.Status.STATUS_COMMITTED: | |
counterUpdateSucceeded.add(Boolean.TRUE); | |
break; | |
} | |
} | |
}); | |
event.setKey(); | |
// this connection insists on transactional, so entities within scope | |
// are immediately attached. set any keys before beginning the transaction | |
txRT.begin(); | |
pmRT.makePersistent(event); | |
FetchAiderUtility.useGetters(event); | |
pmRT.flush(); | |
txRT.commit(); | |
// if insert succeeded, update counters | |
if (!insertSucceeded.isEmpty() && insertSucceeded.get(0)) { | |
tx.begin(); | |
log.info("increment counters"); | |
EventCounterDAO.incrementNumberOfEntities(); | |
VenueCounterDAO.incrementNumberOfEntities(); | |
tx.commit(); | |
} else { | |
log.info("we will not commit transaction 2"); | |
} | |
} finally { | |
if ((txRT != null) && txRT.isActive()) { | |
txRT.rollback(); | |
if ((tx != null) && tx.isActive()) { | |
tx.rollback(); | |
} | |
} | |
if (pmRT != null) { | |
pmRT.close(); | |
} | |
if (pm != null) { | |
pm.close(); | |
} | |
} | |
return event.getKey(); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment