I'm currently experiencing an unexpected behaviour with the Spring transaction manager: the same transaction is assigned to multiple threads.
The scenario is a CXF webservice running in a Tomcat server. The service itself is a Spring bean (singleton). When sending many parallel requests (in my test I used 10) to the server it sometimes happens that two requests (running in different threads) work with the same transaction. Since the logic is designed to use one transaction per request this behaviour causes a faulty behaviour of the application.
Below my service class:
package ...clientsupport_v1;
import...
#WebService(targetNamespace = ...)
#Transactional(rollbackFor = MyException.class)
public class ClientSupportFacade extends AbstractServiceFacade implements ClientSupportService {
private static final Logger LOG = LoggerFactory.getLogger(ClientSupportFacade.class);
#Autowired
private HibernateTransactionManager transactionManager;
#Autowired
private ClientOrderImporter clientOrderImporter;
#Override
public Advice receiveClientPreAdvice(User user, PreAdvice preAdvice) throws MyException {
LOG.debug("PreAdvice: " + preAdvice + ", Transaction: " + getTransactionId() + ", Thread: "
+ Thread.currentThread().getName());
...
return advice;
}
private int getTransactionId() {
return transactionManager.getSessionFactory().getCurrentSession().getTransaction().hashCode();
}
}
receiveClientPreAdvice is the method exposed as service operation.
When calling with multiple threads I get the following log lines:
2013-10-29 13:59:25.135 DEBUG e.h.p.s.c.c.ClientSupportFacade.receiveClientPreAdvice:42 - PreAdvice: PreAdvice [barcode=90000000002161, ...], Transaction: 420660542, Thread: http-bio-8080-exec-9
2013-10-29 13:59:25.135 DEBUG e.h.p.s.c.c.ClientSupportFacade.receiveClientPreAdvice:42 - PreAdvice: PreAdvice [barcode=90000000002163, ...], Transaction: 420660542, Thread: http-bio-8080-exec-8
As you can see two different objects have been received and two different threads are running. Yet the transaction is the same.
How can this happen? And - even more important - how can it be avoided?
From HibernateTransactionManager javadoc:
Binds a Hibernate Session from the specified factory to the thread, potentially allowing for one thread-bound Session per factory ... This transaction manager is appropriate for applications that use a single Hibernate SessionFactory for transactional data access, ...
It may be helpful to post your hibernate configuration but from your code, I can see you are logging getTransaction().hashCode() as Transaction ID assuming they refer to the same transaction object while hashCode() is not required to return different values for different objects. There can be 2 distinct transactions in progress that both have the same hash-code value.
Related
Most of business logic of our product consists in many automatic jobs which perform data loading from DB, processing and final persistence.
Each job is an EJB like the following:
#Stateless
#Local(Job.class)
#TransactionManagement(TransactionManagementType.BEAN)
public class JobImpl {
#Resource
private TimerService timer;
#Resource
private UserTransaction transaction;
#PersistenceUnit
private EntityManagerFactory emf;
#EJB
private BusinessLogicEJB someDependencyEJB;
}
Each job, after is started, is configured to be automatically scheduled every n seconds using EJB TimerService:
this.timer.createIntervalTimer(1000, properties.getTimeout(), new TimerConfig(properties, false));
where properties is an object wrapping that job configuration. In this configuration it is set up how many items the job is supposed to process at each cycle and if multithreading is allowed. If yes, it is also specified the max number of threads and how many items per thread must be processed.
So, when #Timeout annotated method is called, all data waiting to be processed is loaded from DB. If more than max items allowed for a single thread are loaded and if multithreading is enabled, these items are splitted into N threads, each created by
this.timer.createSingleActionTimer(0, new TimerConfig(itemsChunk, false));
which creates a single execution timer. Then, when all threads are created, the EJB father (class implementing job) waits them to finish:
private void waitToComplete(){
while(timer.getTimers().size() > 2){
try {
Thread.sleep(500);
} catch (InterruptedException e) {}
}
}
Each child process calls #Timeout annotated method but it is detected as a child thread and it follows its own branch until it finishes:
#Timeout
public void execute(Timer tm) {
JobProperties properties = (Properties)tm.getInfo();
if(tm.isSubTask()) {
List chunk = tm.getElementsToExecute();
process(chunk);
}
}
where process() is a private method which can use some other injected business logic EJB or the EntityManager and so on.
Finally, a new schedule time is arisen and everything starts again from the beginning.
Now, I was asked to perform some refactoring and we want out EJB Job class as #Singleton while threads processing must not be created by TimerService but by #Asynchronous annotation.
Is is possible to annotate with #Asynchronous a private or a public method of the same EJB (like process()) in order to get a new generated thread?
Actually I created a brand new further EJB bean managed transaction with process() method which has injected EJB someDependencyEJB and uses UserTransaction for DB transaction but this is too bug refactoring.
Is there a way to re-use private methods (or made them public) of the #Singleton EJB to correctly perform with #Asynchronous and without having a "Trasaction already exists" exception each time a new thread is created?
We run or product in Wildfly 10 with EJB 3.1 Java EE 6, and unfortunately we cannot update to Java EE 7.
#Singelton
public Class className {
#Resource
private TransactionSynchronizationRegistry tsr;
#Resource
private Transaction
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public method_A () {
System.out.println(tsr.getTransactionStatus()); // prints 0
method_call_which_throw_persistence_exception();
System.out.println(tsr.getTransactionStatus()); // prints 1
method_B();
}
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public method_B () {
System.out.println(tsr.getTransactionStatus()); // prints 1
}
}
Note:
Transaction status 0 = active.
Transaction status 1 = markedForRollback
As i have attached the code above, 2 methods are present in a Ejb singelton bean which is using container managed transaction. method_A starts which have TransactionAttribute as REQUIRED thus TransactionSynchronizationRegistry prints transaction status as 0 in starting.
After a method call which throw runtime exception transaction status becomes 1 automatically. But when a method_B which have transaction attribute as REQUIRES_NEW is called still TransactionSynchronizationRegistry prints 1.
As per my understanding it should start a new transaction and the trasaction status should show as 0 in method_B?
A direct call on the bean method from a method of the same bean does not go through the transaction interceptor and hence no transaction attribute check is done.
There are 2 ways to solve this:
#Resource
private ManagedExecutorService mes;
....
mes.execute(()->method_B());
....
This will go through the interceptor/proxy and also being in a different thread will automatically start a new transaction. the drawback here is that if the new transaction fails the one in method_A will not be rolled back as they are in different threads
I make a POC with spring-boot-starter-data-jpa and spring-boot-starter-activemq. I would like to push the jms message on the broker (activeMQ) when the jpa transaction was commited.
My code :
UtilsateurService with have the "main" transaction:
#Service
public class UtilisateurService {
#Autowired
private UtilisateurRepository utilisateurRepository;
#Autowired
private SendMessage sendMessage;
#Transactional(rollbackOn = java.lang.Exception.class)
public Utilisateur create(Utilisateur utilisateur) throws Exception {
final Utilisateur result = utilisateurRepository.save(utilisateur);
sendMessage.send("creation utilisateur : " + result.getId());
throw new Exception("rollback");
//return result;
}
}
The SendMessage class witch "manage" Jms message:
#Component
public class SendMessage {
#Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
#Value("${jms.queue.destination}")
private String destinationQueue;
public void send(String msg) {
this.jmsMessagingTemplate.convertAndSend(destinationQueue, msg);
}
}
My main class :
#SpringBootApplication
#EnableJms
#EnableTransactionManagement
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
The JMS message was push on the activeMq broker before exception was throw. So I don't have "rollback" on the broker.
How can I configure to have xa transaction running?
is your jmsTemplate Transacted ?
jmsTemplate.setSessionTransacted(true);
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/jms/support/JmsAccessor.html#setSessionTransacted-boolean-
public void setSessionTransacted(boolean sessionTransacted)
Set the transaction mode that is used when creating a JMS Session.
Default is "false". Note that within a JTA transaction, the parameters
passed to create(Queue/Topic)Session(boolean transacted, int
acknowledgeMode) method are not taken into account. Depending on the
Java EE transaction context, the container makes its own decisions on
these values. Analogously, these parameters are not taken into account
within a locally managed transaction either, since the accessor
operates on an existing JMS Session in this case.
Setting this flag to "true" will use a short local JMS transaction
when running outside of a managed transaction, and a synchronized
local JMS transaction in case of a managed transaction (other than an
XA transaction) being present. This has the effect of a local JMS
transaction being managed alongside the main transaction (which might
be a native JDBC transaction), with the JMS transaction committing
right after the main transaction.
http://www.javaworld.com/article/2077963/open-source-tools/distributed-transactions-in-spring--with-and-without-xa.html
30.2.5 Transaction management
Spring provides a JmsTransactionManager that manages transactions for
a single JMS ConnectionFactory. This allows JMS applications to
leverage the managed transaction features of Spring as described in
Chapter 17, Transaction Management. The JmsTransactionManager performs
local resource transactions, binding a JMS Connection/Session pair
from the specified ConnectionFactory to the thread. JmsTemplate
automatically detects such transactional resources and operates on
them accordingly.
In a Java EE environment, the ConnectionFactory will pool Connections
and Sessions, so those resources are efficiently reused across
transactions. In a standalone environment, using Spring’s
SingleConnectionFactory will result in a shared JMS Connection, with
each transaction having its own independent Session. Alternatively,
consider the use of a provider-specific pooling adapter such as
ActiveMQ’s PooledConnectionFactory class.
JmsTemplate can also be used with the JtaTransactionManager and an
XA-capable JMS ConnectionFactory for performing distributed
transactions. Note that this requires the use of a JTA transaction
manager as well as a properly XA-configured ConnectionFactory! (Check
your Java EE server’s / JMS provider’s documentation.)
Reusing code across a managed and unmanaged transactional environment
can be confusing when using the JMS API to create a Session from a
Connection. This is because the JMS API has only one factory method to
create a Session and it requires values for the transaction and
acknowledgment modes. In a managed environment, setting these values
is the responsibility of the environment’s transactional
infrastructure, so these values are ignored by the vendor’s wrapper to
the JMS Connection. When using the JmsTemplate in an unmanaged
environment you can specify these values through the use of the
properties sessionTransacted and sessionAcknowledgeMode. When using a
PlatformTransactionManager with JmsTemplate, the template will always
be given a transactional JMS Session.
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/jms.html#jms-tx
Hassen give the solution. So I change the SendMessage class to :
#Component
public class SendMessage {
private final JmsMessagingTemplate jmsMessagingTemplate;
#Value("${jms.queue.destination}")
private String destinationQueue;
#Autowired
public SendMessage(JmsMessagingTemplate jmsMessagingTemplate) {
this.jmsMessagingTemplate = jmsMessagingTemplate;
this.jmsMessagingTemplate.getJmsTemplate().setSessionTransacted(true);
}
public void send(String msg) {
this.jmsMessagingTemplate.convertAndSend(destinationQueue, msg);
}
}
I have a question regarding #Transactional annotation.
Nothing special defined, so as I understand is PROPAGATION_REQUIRED
Let’s say I have a transactional annotation which on both service and dao layer.
Service
#Transactional
public long createStudentInDB(Student student) {
final long id = addStudentToDB (student);
addStudentToCourses (id, student.getCourseIds());
return id;
}
private long addStudentToDB (Student student) {
StudentEntity entity = new StudentEntity ();
convertToEntity(student, entity);
try {
final id = dao.create(entity);
} catch (Exception e){
//
}
return id;
}
private void addStudentToCourses (long studentId, List<String> coursesIds){
//add user to group
if(coursesIds!= null){
List<StudentCourseEntity> studentCourses = new ArrayList<>();
for(String coursesId: coursesIds){
StudentCourseEntity entity = new StudentCourseEntity ();
entity.setCourseId(coursesId);
entity.setStudentId(userId);
studentCourses.add(studentId);
}
anotherDao.saveAll(studentCourses);
}
}
DAO
#Transactional
public UUID create(StudentEntity entity) {
if ( entity == null ) { throw new Exception(//…); }
getCurrentSession().save(entity);
return entity.getId();
}
ANOTHER DAO:
#Transactional
public void saveAll(Collection< StudentCourseEntity > studentCourses) {
List< StudentCourseEntity > result = new ArrayList<>();
if(studentCourses!= null) {
for (StudentCourseEntity studentCourse : studentCourses) {
if (studentCourse!= null) {
save(studentCourse);
}
}
}
}
Despite the fact that’s not optimal, it seems it causing deadlocks.
Let’s say I have max 2 connections to the database.
And I am using 3 different threads to run the same code.
Thread-1 and thread-2 receive a connection, thread-3 is not getting any connection.
More than that, it seems that thread-1 become stuck when trying to get a connection in dao level, same for thread-2. Causing a deadlock.
I was sure that by using propagation_required this would not happen.
Am I missing something?
What’s the recommendation for something like that? Is there a way I can have #transactional on both layers? If not which is preferred?
Thanks
Fabrizio
As the dao.doSomeStuff is expected to be invoked from within other transactions I would suggest you to configure this method as:
#Transaction(propagation=REQUIRES_NEW)
Thanks to that the transaction which is invoking this method will halted until the one with REQUIRES_NEW will be finished.
Not sure if this is the fix for your particular deadlock case but your example fits this particular set-up.
You are right, Propagation.REQUIRED is the default. But that also means that the second (nested) invocation on dao joins / reuses the transaction created on service level. So there is no need to create another transaction for the nested call.
In general Spring (on high level usage) should manage resource handling by forwarding it to the underlying ORM layer:
The preferred approach is to use Spring's highest level template based
persistence integration APIs or to use native ORM APIs with
transaction- aware factory beans or proxies for managing the native
resource factories. These transaction-aware solutions internally
handle resource creation and reuse, cleanup, optional transaction
synchronization of the resources, and exception mapping. Thus user
data access code does not have to address these tasks, but can be
focused purely on non-boilerplate persistence logic.
Even if you handle it on your own (on low level API usage) the connections should be reused:
When you want the application code to deal directly with the resource
types of the native persistence APIs, you use these classes to ensure
that proper Spring Framework-managed instances are obtained,
transactions are (optionally) synchronized, and exceptions that occur
in the process are properly mapped to a consistent API.
...
If an existing transaction already has a connection synchronized
(linked) to it, that instance is returned. Otherwise, the method call
triggers the creation of a new connection, which is (optionally)
synchronized to any existing transaction, and made available for
subsequent reuse in that same transaction.
Source
Maybe you have to find out what is happening down there.
Each Session / Unit of Work will be bound to a thread and released (together with the assigned connection) after the transaction has ended. Of course when your thread gets stuck it won't release the connection.
Are you sure that this 'deadlock' is caused by this nesting? Maybe that has another reason. Do you have some test code for this example? Or a thread dump or something?
#Transactional works by keeping ThreadLocal state, which can be accessed by the (Spring managed) Proxy EntityManager. If you are using Propagation.REQUIRED (the default), and you have a non-transactional method which calls two different DAOs (or two Transactional methods on the same DAO), you will get two transactions, and two calls to acquire a pooled connection. You may get the same connection twice or two different connections, but you should only use one connection at the time.
If you call two DAOs from a #Transactional method, there will only be one transaction, as the DAO will find and join the existing transaction found in the ThreadLocal state, again you only need one connection from the pool.
If you get a deadlock then something is very wrong, and you may want to debug when your connections and transaction are created. A transaction is started by calling Connection.setAutoCommit(false), in Hibernate this happens in org.hibernate.resource.jdbc.internal.AbstractLogicalConnectionImplementor#begin(). Connections are managed by a class extending org.hibernate.resource.jdbc.internal.AbstractLogicalConnectionImplementor so these are some good places to put a break point, and trace the call-stack back to your code to see which lines created connections.
I am using annotation driven transaction management with Spring JDBC.
I would like to have Spring throw an exception when by mistake I forgot to annotate with #Transactional a service method that inserts/updates/deletes.
By default data can be inserted/updated/deleted even not within a transaction.
You can use Propagation.MANDATORY in your DAO layer.
Propagation.MANDATORY will not start a transaction. It will check whether perticular method is attached to a transaction or not, if not container will throw an exception.
According to the documentation (Spring docs) it's just metadata to give an indication that the method or interface can be configured by something that is 'transactionally aware' (ie
With just tx:annotation-driven and no #Transactional attributes I believe you get the "default" transactionality applied:
Propagation setting is REQUIRED.
Isolation level is DEFAULT.
Transaction is read/write.
Transaction timeout defaults to the default timeout of the underlying transaction system, or none if timeouts are not supported.
ny RuntimeException triggers rollback, and any checked Exception does not.
Assuming you're using the tx:annotation to drive it via a transaction manager then missing out the #Transactional attribute means you can't apply such properties as readOnly, isolation, propagation,rollbackFor, noRollbackFor etc.
I believe that MVC is slightly different - the hibernate session is tied directly to the MVC request - ie when the request is received the transaction starts.
Back to your example, the code for getSession() in HibernateDAOSupport is as follows:
protected final Session getSession()
throws DataAccessResourceFailureException, IllegalStateException
{
return getSession(this.hibernateTemplate.isAllowCreate());
}
Which in turn calls to:
/**
* Obtain a Hibernate Session, either from the current transaction or
* a new one. The latter is only allowed if "allowCreate" is true.
*.......
protected final Session getSession()
throws DataAccessResourceFailureException, IllegalStateException {
return getSession(this.hibernateTemplate.isAllowCreate());
}
which ultimately calls to :
/**
* ....
* #param allowCreate whether a non-transactional Session should be created
* when no transactional Session can be found for the current thread
* ....
*/
private static Session doGetSession(
SessionFactory sessionFactory, Interceptor entityInterceptor,
SQLExceptionTranslator jdbcExceptionTranslator, boolean allowCreate)
Fundamentally, a Transaction:Session is tied 1:1 afaik, and the only way to run without a transaction is by using say JBoss which has a 'baked in' persistence layer which provides the transactionality for you (under the covers). Even if you call getQuery() after getSession() you still effectively have a transaction occuring as it's a JDBC/Hibernate connection.