Search in sources :

Example 1 with Transactional

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();
        }
    }
}
Also used : OptimisticLockException(javax.persistence.OptimisticLockException) Timer(com.codahale.metrics.Timer) StaleStateException(org.hibernate.StaleStateException) PersistenceException(javax.persistence.PersistenceException) GenericJDBCException(org.hibernate.exception.GenericJDBCException) Transactional(com.peterphi.std.guice.database.annotation.Transactional) LockAcquisitionException(org.hibernate.exception.LockAcquisitionException)

Example 2 with Transactional

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();
}
Also used : Criteria(org.hibernate.Criteria) Transactional(com.peterphi.std.guice.database.annotation.Transactional)

Example 3 with Transactional

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());
}
Also used : WebQuery(com.peterphi.std.guice.restclient.jaxb.webquery.WebQuery) Test(org.junit.Test) Transactional(com.peterphi.std.guice.database.annotation.Transactional)

Example 4 with Transactional

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());
}
Also used : WebQuery(com.peterphi.std.guice.restclient.jaxb.webquery.WebQuery) Test(org.junit.Test) Transactional(com.peterphi.std.guice.database.annotation.Transactional)

Example 5 with Transactional

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()));
}
Also used : WebQuery(com.peterphi.std.guice.restclient.jaxb.webquery.WebQuery) Test(org.junit.Test) Transactional(com.peterphi.std.guice.database.annotation.Transactional)

Aggregations

Transactional (com.peterphi.std.guice.database.annotation.Transactional)46 UserEntity (com.peterphi.usermanager.db.entity.UserEntity)13 WebQuery (com.peterphi.std.guice.restclient.jaxb.webquery.WebQuery)11 TemplateCall (com.peterphi.std.guice.web.rest.templating.TemplateCall)9 RoleEntity (com.peterphi.usermanager.db.entity.RoleEntity)9 AuthConstraint (com.peterphi.std.guice.common.auth.annotations.AuthConstraint)8 OAuthServiceEntity (com.peterphi.usermanager.db.entity.OAuthServiceEntity)5 AuthenticationFailureException (com.peterphi.usermanager.guice.authentication.AuthenticationFailureException)5 OAuthSessionEntity (com.peterphi.usermanager.db.entity.OAuthSessionEntity)4 Test (org.junit.Test)4 ResourceInstanceEntity (com.peterphi.servicemanager.service.db.entity.ResourceInstanceEntity)3 ResourceTemplateEntity (com.peterphi.servicemanager.service.db.entity.ResourceTemplateEntity)3 Criteria (org.hibernate.Criteria)3 List (java.util.List)2 DateTime (org.joda.time.DateTime)2 Timer (com.codahale.metrics.Timer)1 Inject (com.google.inject.Inject)1 Singleton (com.google.inject.Singleton)1 ServiceInstanceEntity (com.peterphi.servicemanager.service.db.entity.ServiceInstanceEntity)1 ResourceNetworkConfig (com.peterphi.servicemanager.service.guice.ResourceNetworkConfig)1