You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
After deep investigation how Spring TX and Groovy work together, I found out that TransactionInterceptor gets applied to methods inherited from groovy.lang.GroovyObject.
While stressing the system with requests, it can also be seen, that upon calling these internal Groovy methods, Spring TX perform commits, which eventually degrades performance. The stack trace below was captured from the execution of the following piece of code.
Before userService.findByAuthenticationToken() would have been executed, the Groovy runtime called getMetaClass() internally as part of call() in order to fetch the meta data of the target object.
"http-nio-8080-exec-10" #89 daemon prio=5 os_prio=0 tid=0x00007f33e0009000 nid=0x3b5c runnable [0x00007f33f52b9000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
...
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2447)
...
at com.zaxxer.hikari.proxy.ConnectionProxy.commit(ConnectionProxy.java:300)
...
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:757)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:726)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:478)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:272)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy144.getMetaClass(Unknown Source)
at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.nonParamCheck(PogoMetaMethodSite.java:79)
at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.checkCall(PogoMetaMethodSite.java:92)
at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.call(PogoMetaMethodSite.java:66)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:122)
at app.web.security.AppAuthenticationProvider.authenticate(AppAuthenticationProvider.groovy:39)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:156)
Workaround
Currently the workaround is in the client's system is to override AnnotationTransactionAttributeSource and customize method getTransactionAttribute() in the following way.
public class GroovyAwareAnnotationTransactionAttributeSource extends AnnotationTransactionAttributeSource {
private static final Logger log = LoggerFactory.getLogger(GroovyAwareAnnotationTransactionAttributeSource.class);
@Override
public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {
if (!ClassUtils.isUserLevelMethod(method)) {
if (log.isTraceEnabled()) {
log.trace("Transaction skipped for non-userlevel method {}", method.toString());
}
return null;
}
return super.getTransactionAttribute(method, targetClass);
}
}
After that some manual tweaking is also necessary to replace tx:annotation-driven/.
<!-- Replace <tx:annotation-driven/> with manual config in order to work a Spring bug around -->
<aop:config/>
<bean id="transactionAttributeSource" class="app
.core.tx.GroovyAwareAnnotationTransactionAttributeSource"/>
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManagerBeanName" value="transactionManager"/>
<property name="transactionAttributeSource" ref="transactionAttributeSource"/>
</bean>
<bean class="org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor">
<property name="adviceBeanName" value="transactionInterceptor"/>
<property name="transactionAttributeSource" ref="transactionAttributeSource"/>
</bean>
We exclude GroovyObject methods for a couple of other TransactionAttributeSource implementations already, but not for AnnotationTransactionAttributeSource since we expected it to be clearly annotation-driven. However, I suppose you are using class-level @Transactional markers? In this case, Groovy methods would indeed be considered transactional, I suppose... We can certainly fine-tune this for 4.3.
I've applied this at a slightly different level: specifically for class-level attributes in AbstractFallbackTransactionAttributeSource.computeTransactionAttribute. For explicitly annotated methods, let's rather consider those applicable in any case, even if not user-level.
As for the test setup in your pull request, it is rather unfortunate to have to turn the entire spring-test module over to cross-compilation here. I've used a unit test arrangement instead, using a custom Java implementation of the GroovyObject interface.
I've applied analogous changes to our AbstractFallbackCacheOperationSource, making AnnotationCacheOperationSource behave the same way with respect to synthetic Groovy methods.
László Csontos opened SPR-14095 and commented
How to reproduce
Issue
After deep investigation how Spring TX and Groovy work together, I found out that TransactionInterceptor gets applied to methods inherited from groovy.lang.GroovyObject.
Switching on debugging reveals that.
While stressing the system with requests, it can also be seen, that upon calling these internal Groovy methods, Spring TX perform commits, which eventually degrades performance. The stack trace below was captured from the execution of the following piece of code.
Before userService.findByAuthenticationToken() would have been executed, the Groovy runtime called getMetaClass() internally as part of call() in order to fetch the meta data of the target object.
Workaround
Currently the workaround is in the client's system is to override AnnotationTransactionAttributeSource and customize method getTransactionAttribute() in the following way.
After that some manual tweaking is also necessary to replace tx:annotation-driven/.
Affects: 4.1.7
Reference URL: https://github.com/laszlocsontos/spring-framework/tree/SPR-14095
The text was updated successfully, but these errors were encountered: