We had a production incident that resulted in a bunch of threads deadlocking and the server stopped working. To try and investigate, I tested some stuff with different spring transactional propagations, and if I'm not mistaken, the REQUIRES_NEW propagation will start two connections if there is no existing transaction at all. Is this correct?? I tried googling, but found no information about this.
I made a test. Here's a sample class:
package test;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class TheService {
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void doSomething() {
System.out.println("Here I am doing something.");
}
}
Here is the unit test that I made:
package test;
import javax.annotation.Resource;
import org.junit.Test;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
@ContextConfiguration(locations = {"classpath:test.xml"})
public class TheServiceTest extends AbstractTransactionalJUnit4SpringContextTests {
@Resource
private TheService theService;
@Test
public void test() {
theService.doSomething();
}
}
And last but not least, here is my test xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd"
default-autowire="byName">
<context:component-scan base-package="test" />
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="operator.entityManagerFactory" />
</bean>
<bean id="operator.entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="operatorPersistenceUnit" />
<property name="dataSource" ref="operator.dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="false" />
<property name="generateDdl" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.H2Dialect" />
</bean>
</property>
</bean>
<bean id="operator.dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:mem:operator" />
<property name="username" value="sa" />
<property name="password" value="" />
<property name="maxActive" value="1" /> <!-- NOTE -->
</bean>
</beans>
The reason why I want REQUIRES_NEW for a method is because it is vital to not get any dirty reads from it, and it can be executed from both within another transaction and outside.
If I keep the maxActive property at 1, this test will deadlock and never print anything. If I change it to 2 however, the test will go through.
The reason why this is a concern is that even if I set maxActive to a much higher value, with enough threads waiting to execute this method, they can all end up occupy one connection each and wait for a second one.
Have I done something wrong? Have I misunderstood anything?
I appreciate any help! Thanks!
REQUIRES_NEW.. - M. Deinumnew Exception().printStackTrace()and I look for the TransactionInterceptor. This, I cannot see when adding it in the test method. - user3306066AbstractTransactionalJUnit4SpringContextTestsdoes... thatTransactionalpart of the classname is there for a reason. There are more ways to work with transactions. For tests that is done by theTransactionalTestExecutionListenerwhich manually start/stops transactions. If you don't want your tests to be transactional don't extendAbstractTransactionalJUnit4SpringContextTests. - M. Deinum