use of com.peterphi.std.guice.database.annotation.Transactional in project stdlib by petergeneric.
the class TransactionMethodInterceptor method invoke.
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
if (sessionProvider.get().getTransaction().getStatus() == TransactionStatus.ACTIVE) {
// allow silent joining of enclosing transactional methods (NOTE: this ignores the current method's txn-al settings)
if (log.isTraceEnabled())
log.trace("Joining existing transaction to call " + invocation.getMethod().toGenericString());
return invocation.proceed();
} else {
Timer.Context callTimer = calls.time();
final String tracingId = Tracing.log("TX:begin", () -> invocation.getMethod().toGenericString());
try {
final Transactional annotation = readAnnotation(invocation);
// After the max attempts for auto retry are exhausted we'll fall back on the non-retrying default behaviour
if (annotation.autoRetry()) {
// Try all but the last attempt
final int retries = Math.max(0, annotation.autoRetryCount() - 1);
// N.B. more aggressive than the @Retry annotation implements
long backoff = 1000;
final double multiplier = 1.5;
for (int attempt = 0; attempt < retries; attempt++) {
try {
return createTransactionAndExecuteMethod(invocation, annotation);
} catch (LockAcquisitionException | StaleStateException | GenericJDBCException | OptimisticLockException e) {
if (log.isTraceEnabled())
log.warn("@Transactional caught exception " + e.getClass().getSimpleName() + "; retrying...", e);
else
log.warn("@Transactional caught exception " + e.getClass().getSimpleName() + "; retrying...");
Tracing.logOngoing(tracingId, "TX:exception:retryable", () -> e.getClass().getSimpleName());
try {
Thread.sleep(backoff);
} catch (InterruptedException ie) {
throw new RuntimeException("Interrupted while attempting a @Transactional retry!", ie);
}
// Increase backoff for the next exception
backoff *= multiplier;
} catch (PersistenceException e) {
// Handle generic exception (usually one that wraps another exception)
if (e.getCause() != null && (isSqlServerSnapshotConflictError(e) || isDeadlockError(e))) {
if (log.isTraceEnabled())
log.warn("@Transactional caught exception PersistenceException wrapping " + e.getCause().getClass().getSimpleName() + "; retrying...", e);
else
log.warn("@Transactional caught exception PersistenceException wrapping " + e.getCause().getClass().getSimpleName() + "; retrying...");
Tracing.logOngoing(tracingId, "TX:exception:retryable:wrapped", () -> e.getCause().getClass().getSimpleName());
try {
Thread.sleep(backoff);
} catch (InterruptedException ie) {
throw new RuntimeException("Interrupted while attempting a @Transactional retry!", ie);
}
// Increase backoff for the next exception
backoff *= multiplier;
} else {
Tracing.logOngoing(tracingId, "TX:exception:fatal", () -> e.getClass().getSimpleName());
// rethrow because we won't handle this
throw e;
}
}
}
}
Tracing.logOngoing(tracingId, "TX:last-try", null);
// Run without further retries
return createTransactionAndExecuteMethod(invocation, annotation);
} finally {
Tracing.logOngoing(tracingId, "TX:quit", null);
callTimer.stop();
}
}
}
use of com.peterphi.std.guice.database.annotation.Transactional in project stdlib by petergeneric.
the class MyTestDaoImpl method countWithAInName.
/**
* Run
* @return
*/
@Transactional(readOnly = true)
public long countWithAInName() {
Criteria criteria = createCriteria();
criteria.add(Subqueries.propertyIn("id", DetachedCriteriaHelper.wrap(createCriteria().setProjection(Projections.id()).add(Restrictions.like("name", "%a%")))));
return criteria.list().size();
}
use of com.peterphi.std.guice.database.annotation.Transactional in project stdlib by petergeneric.
the class LargeTableQueryTest method testSearchComplexPK.
/**
* Test that searching works even with a complex primary key
*/
@Test
@Transactional
public void testSearchComplexPK() {
complexPKdao.save(new LargeTableComplexPKEntity("Alice"));
complexPKdao.save(new LargeTableComplexPKEntity("Bob"));
complexPKdao.save(new LargeTableComplexPKEntity("Carol"));
complexPKdao.save(new LargeTableComplexPKEntity("Dave"));
complexPKdao.save(new LargeTableComplexPKEntity("Eve"));
assertEquals(2, complexPKdao.findByUriQuery(new WebQuery().contains("name", "a")).getList().size());
}
use of com.peterphi.std.guice.database.annotation.Transactional in project stdlib by petergeneric.
the class LargeTableQueryTest method testSearchComplexPKIDs.
/**
* Test that searching for IDs works even with a complex primary key
*/
@Test
@Transactional
public void testSearchComplexPKIDs() {
complexPKdao.save(new LargeTableComplexPKEntity("Alice"));
complexPKdao.save(new LargeTableComplexPKEntity("Bob"));
complexPKdao.save(new LargeTableComplexPKEntity("Carol"));
complexPKdao.save(new LargeTableComplexPKEntity("Dave"));
complexPKdao.save(new LargeTableComplexPKEntity("Eve"));
final List<SomePrimaryKey> results = complexPKdao.findIdsByUriQuery(new WebQuery().contains("name", "a")).getList();
assertEquals("expect 2 results", 2, results.size());
assertEquals("expect results to be of id type", SomePrimaryKey.class, results.get(0).getClass());
assertEquals("expect results to be of id type", SomePrimaryKey.class, results.get(1).getClass());
}
use of com.peterphi.std.guice.database.annotation.Transactional in project stdlib by petergeneric.
the class LargeTableQueryTest method testSearchForPrimaryKeyWorks.
/**
* Test that searching works with a simple primary key
*/
@Test
@Transactional
public void testSearchForPrimaryKeyWorks() {
dao.save(new LargeTableSimplePKEntity("Alice"));
dao.save(new LargeTableSimplePKEntity("Bob"));
dao.save(new LargeTableSimplePKEntity("Carol"));
dao.save(new LargeTableSimplePKEntity("Dave"));
dao.save(new LargeTableSimplePKEntity("Eve"));
// Try a regular query
assertEquals("custom-coded count should return 2", 2, dao.countWithAInName());
// Now try a web query
final ConstrainedResultSet<Long> result = dao.findIdsByUriQuery(new WebQuery().computeSize(true).contains("name", "a"));
assertEquals("total results for generic query should be 2", Long.valueOf(2), result.getTotal());
assertEquals("generic get IDs should return 2 results", new ArrayList<>(Arrays.asList(3L, 4L)), new ArrayList<>(result.getList()));
}
Aggregations