From 282ac10262c2c8f13f0cb949191d0e7b1370f330 Mon Sep 17 00:00:00 2001 From: Marcel Hanser Date: Wed, 19 May 2021 14:46:13 +0200 Subject: [PATCH] Add method based caching for transaction configuration Resolves: quarkusio#15752 --- .../TransactionalInterceptorBase.java | 60 +++++++++++-------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/interceptor/TransactionalInterceptorBase.java b/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/interceptor/TransactionalInterceptorBase.java index 22e9554c3982e4..e6a009384ab032 100644 --- a/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/interceptor/TransactionalInterceptorBase.java +++ b/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/interceptor/TransactionalInterceptorBase.java @@ -2,10 +2,14 @@ import java.io.Serializable; import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionStage; +import java.util.concurrent.ConcurrentHashMap; import javax.inject.Inject; import javax.interceptor.InvocationContext; @@ -37,6 +41,7 @@ public abstract class TransactionalInterceptorBase implements Serializable { private static final long serialVersionUID = 1L; private static final Logger log = Logger.getLogger(TransactionalInterceptorBase.class); + private final Map cache = new ConcurrentHashMap<>(); @Inject TransactionManager transactionManager; @@ -69,8 +74,7 @@ public Object intercept(InvocationContext ic) throws Exception { * Method handles CDI types to cover cases where extensions are used. In * case of EE container uses reflection. * - * @param ic - * invocation context of the interceptor + * @param ic invocation context of the interceptor * @return instance of {@link Transactional} annotation or null */ private Transactional getTransactional(InvocationContext ic) { @@ -107,35 +111,21 @@ protected Object invokeInOurTx(InvocationContext ic, TransactionManager tm) thro protected Object invokeInOurTx(InvocationContext ic, TransactionManager tm, RunnableWithException afterEndTransaction) throws Exception { - TransactionConfiguration configAnnotation = getTransactionConfiguration(ic); + Integer timeoutConfiguredForMethod = cache.computeIfAbsent(ic.getMethod(), + (m) -> extractTransactionConfigurationTimeoutFromAnnotation(ic)); + int currentTmTimeout = ((CDIDelegatingTransactionManager) transactionManager).getTransactionTimeout(); - boolean restoreTimeout = false; - if (configAnnotation != null) { - Integer newTimeout = null; - if (!configAnnotation.timeoutFromConfigProperty().equals(TransactionConfiguration.UNSET_TIMEOUT_CONFIG_PROPERTY)) { - Optional configTimeout = ConfigProvider.getConfig() - .getOptionalValue(configAnnotation.timeoutFromConfigProperty(), Integer.class); - if (configTimeout.isPresent()) { - newTimeout = configTimeout.get(); - } else if (log.isDebugEnabled()) { - log.debug("Configuration property '" + configAnnotation.timeoutFromConfigProperty() - + "' was not provided, so it will not affect the transaction's timeout."); - } - } - if ((newTimeout == null) && (configAnnotation.timeout() != TransactionConfiguration.UNSET_TIMEOUT)) { - newTimeout = configAnnotation.timeout(); - } - if (newTimeout != null) { - tm.setTransactionTimeout(newTimeout); - restoreTimeout = true; - } + + if (Objects.nonNull(timeoutConfiguredForMethod)) { + tm.setTransactionTimeout(timeoutConfiguredForMethod); } + Transaction tx; try { tm.begin(); tx = tm.getTransaction(); } finally { - if (restoreTimeout) { + if (Objects.nonNull(timeoutConfiguredForMethod)) { tm.setTransactionTimeout(currentTmTimeout); } } @@ -187,6 +177,26 @@ protected Object invokeInOurTx(InvocationContext ic, TransactionManager tm, Runn return ret; } + private Integer extractTransactionConfigurationTimeoutFromAnnotation(InvocationContext ic) { + TransactionConfiguration configAnnotation = getTransactionConfiguration(ic); + if (!configAnnotation.timeoutFromConfigProperty().equals(TransactionConfiguration.UNSET_TIMEOUT_CONFIG_PROPERTY)) { + Optional configTimeout = ConfigProvider.getConfig() + .getOptionalValue(configAnnotation.timeoutFromConfigProperty(), Integer.class); + if (configTimeout.isPresent()) { + return configTimeout.get(); + } else if (log.isDebugEnabled()) { + log.debug("Configuration property '" + configAnnotation.timeoutFromConfigProperty() + + "' was not provided, so it will not affect the transaction's timeout."); + } + } + + if ((configAnnotation.timeout() != TransactionConfiguration.UNSET_TIMEOUT)) { + return configAnnotation.timeout(); + } + + return null; + } + protected Object handleAsync(TransactionManager tm, Transaction tx, InvocationContext ic, Object ret, RunnableWithException afterEndTransaction) throws Exception { // Suspend the transaction to remove it from the main request thread @@ -355,7 +365,7 @@ protected void resetUserTransactionAvailability(boolean previousUserTransactionA * An utility method to throw any exception as a {@link RuntimeException}. * We may throw a checked exception (subtype of {@code Throwable} or {@code Exception}) as un-checked exception. * This considers the Java 8 inference rule that states that a {@code throws E} is inferred as {@code RuntimeException}. - * + *

* This method can be used in {@code throw} statement such as: {@code throw sneakyThrow(exception);}. */ @SuppressWarnings("unchecked")