@Override
	public <R> R transactNew(final ObjectifyImpl parent, int limitTries, final Work<R> work) {
		Preconditions.checkArgument(limitTries >= 1);
		final int ORIGINAL_TRIES = limitTries;

		while (true) {
			try {
				return transactOnce(parent, work);
			} catch (DatastoreException ex) {

				// This doesn't work because the SDK considers all transactions to be non-retryable. Objectify has always
				// assumed that transactions are idempotent and retries accordingly. So we have to explicitly check against
				// code 10, which is ABORTED. https://cloud.google.com/datastore/docs/concepts/errors
//				if (!ex.isRetryable())
//					throw ex;
				// I hate this so much. Sometimes the transaction gets closed by the datastore during contention and
				// then it proceeds to freak out and 503.
				if (Code.ABORTED.getNumber() == ex.getCode() || (Code.INVALID_ARGUMENT.getNumber() == ex.getCode() && ex.getMessage().contains("transaction closed"))) {
					// Continue to retry logic
				} else {
					throw ex;
				}

				if (--limitTries > 0) {
					log.warn("Retrying {} failure for {}: {}", ex.getReason(), work, ex);
					log.trace("Details of transaction failure", ex);
					try {
						// Do increasing backoffs with randomness
						Thread.sleep(Math.min(10000, (long) (0.5 * Math.random() + 0.5) * 200 * (ORIGINAL_TRIES - limitTries + 2)));
					} catch (InterruptedException ignored) {
					}
				} else {
					throw new DatastoreException(ex.getCode(), "Failed retrying datastore " + ORIGINAL_TRIES + " times ", ex.getReason(), ex);
				}
			}
		}
	}
@ Override public < R > R transactNew ( final ObjectifyImpl parent , int limitTries , final Work < R > work ) { Preconditions . checkArgument ( limitTries >= 1 ) ; final int ORIGINAL_TRIES = limitTries ; while ( true ) { try { return transactOnce ( parent , work ) ; } catch ( DatastoreException ex ) { if ( Code . ABORTED . getNumber ( ) == ex . getCode ( ) || ( Code . INVALID_ARGUMENT . getNumber ( ) == ex . getCode ( ) && ex . getMessage ( ) . contains ( "transaction closed" ) ) ) { } else { throw ex ; } if ( -- limitTries > 0 ) { log . warn ( "Retrying {} failure for {}: {}" , ex . getReason ( ) , work , ex ) ; log . trace ( "Details of transaction failure" , ex ) ; try { Thread . sleep ( Math . min ( 10000 , ( long ) ( 0.5 * Math . random ( ) + 0.5 ) * 200 * ( ORIGINAL_TRIES - limitTries + 2 ) ) ) ; } catch ( InterruptedException ignored ) { } } else { throw new DatastoreException ( ex . getCode ( ) , "Failed retrying datastore " + ORIGINAL_TRIES + " times " , ex . getReason ( ) , ex ) ; } } } }
/* (non-Javadoc)
@see com.googlecode.objectify.impl.Transactor#transactNew(com.googlecode.objectify.impl.ObjectifyImpl, int, com.googlecode.objectify.Work)