I'm making a very simple example to test Spring isolation. I have 2 very similar Schedule classes:
@Service("manager1")
public class Manager1 {
private Service1 service1;
@Scheduled(fixedDelay = 15000)
public void sendScheduledCampaigns() {
service1.changeCredits();
}
...
@Service("manager2")
public class Manager2 {
private Service2 service2;
@Scheduled(fixedDelay = 15000)
public void sendScheduledCampaigns() {
service2.changeCredits();
}
...
Also, I have 2 very similar service classes:
@Service("service1")
@Transactional
public class Service1 {
private UserService userService;
private static Logger log = Logger.getLogger(Service1.class);
public void changeCredits() {
User user = userService.getUserById(1);
log.info("Service1 previous credits: " + user.getCredits());
int newCredit = user.getCredits() + 5;
user.setCredits(newCredit);
log.info("Service1 new credits: " + user.getCredits());
}
...
@Service("service2")
@Transactional
public class Service2 {
private UserService userService;
private static Logger log = Logger.getLogger(Service2.class);
public void changeCredits() {
User user = userService.getUserById(1);
log.info("Service2 previous credits: " + user.getCredits());
int newCredit = user.getCredits() + 5;
user.setCredits(newCredit);
log.info("Service2 new credits: " + user.getCredits());
}
After running this code, I can see that when both transactions are executed at same time, both transactions are getting the same value from the database. So, they are ignoring the modification of the other one. I suppose this is an error in the transaction isolation:
INFO 27 Feb 2014 14:14:22 - Service1 previous credits: 0
INFO 27 Feb 2014 14:14:22 - Service2 previous credits: 0
INFO 27 Feb 2014 14:14:22 - Service1 new credits: 5
INFO 27 Feb 2014 14:14:22 - Service2 new credits: 5
Here the credits value should be 10.
After this, as both processes are not being executed at same time, the calculation starts being normal:
Feb 27, 2014 2:14:23 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-bio-8080"]
Feb 27, 2014 2:14:23 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["ajp-bio-8009"]
Feb 27, 2014 2:14:23 PM org.apache.catalina.startup.Catalina start
INFO: Server startup in 10698 ms
INFO 27 Feb 2014 14:14:37 - Service2 previous credits: 5
INFO 27 Feb 2014 14:14:37 - Service2 new credits: 10
INFO 27 Feb 2014 14:14:38 - Service1 previous credits: 10
INFO 27 Feb 2014 14:14:38 - Service1 new credits: 15
I'm working with MYSQL and these are my tasks and transactions configurations:
<task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
<task:executor id="myExecutor" pool-size="100"/>
<task:scheduler id="myScheduler" pool-size="100"/>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="myDataSource" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>
<prop key="hibernate.show_sql">false</prop>
</props>
</property>
<property name="packagesToScan" value="test.domain" />
</bean>
For some reason my default isolation level is null. I got it from this:
log.info("isolation: " +TransactionSynchronizationManager.getCurrentTransactionIsolationLevel());
So, I tried to change the isolation level for both services:
@Service("service1")
@Transactional(isolation=Isolation.REPEATABLE_READ)
public class Service1 {
@Service("service2")
@Transactional(isolation=Isolation.REPEATABLE_READ)
public class Service2 {
The result is still wrong.
Do you have any idea about why this is happening?
Thanks in advance.