Announcement Announcement Module
Collapse
No announcement yet.
AMQP Transactions and Hibernate Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • AMQP Transactions and Hibernate

    I have an app that uses Hibernate and AMQP. The transaction manager is configured as :

    Code:
    <bean id="transactionManager"
    		class="org.springframework.orm.hibernate3.HibernateTransactionManager"
    		p:sessionFactory-ref="sessionFactory" p:dataSource-ref="dataSource" />
    I then have the following:

    Code:
    @Configuration
    public class MyConfig {
        
        @Resource(name="transactionManager")
        private PlatformTransactionManager txManager;
    
        @Autowired
        private MyListener messageListener;
    
        @Bean
        public SimpleMessageListenerContainer mySMLC()
        {
            final SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
            container.setConnectionFactory(rabbitConnectionFactory);
            container.setQueueNames("myQueue");
    
            final MessageListenerAdapter adapter = new MessageListenerAdapter(messageListener);
            adapter.setMessageConverter(converter);
            container.setMessageListener(adapter);
            container.setChannelTransacted(true);
            container.setTransactionManager(txManager);
            return container;
        }
    
    ...
    
       @Component
        public class MyListener {
          
            @Resource(name="myServiceImpl")
             private MyService service;
    
             public void handleMessage(MyDTO convertedMessage){
                Resource r = new Resource(convertedMessage.getCategory());
                ... // set some other properties on r
                service.save(r); // this is more complicated in the real code
             }
       }
    
       ...
    
       @Service
       public class MyServiceImpl implements MyService {
    
          @Override
          @Transactional
          public void save(Resource resource){
               // do some work and save to DB
          }
        }
    }
    My listener receives a message and delegates to a service which creates some data and saves it in the DB. The method that is called is annotated with @Transactional. What I want to know is, say the call to save() completes without any issues and so does the handleMessage() method in the listener, but somewhere higher up in the execution chain an exception is encountered and the message is never acknowledged. Will this exception cause the work in the save() method to be rolled back? If the broker never receives the acknowledgement, it will resend the message causing possible duplicate data to be saved. Another example is, say that the save() and handle() methods complete without any issues, but before the message is acknowledged, the app goes down for some reason. Again this will cause the broker to resend the message (the queue is durable using a topic exchange). Should I worry about this scenario?

  • #2
    There is always a possibility of duplication transactions unless you are using an XA transaction manager and XA-capable resources (RabbitMQ does not support XA).

    Spring AMQP implements the 'Best Efforts 1PC pattern' (http://www.javaworld.com/javaworld/j...nsactions.html).

    The DB transaction is started before the receive and the DB commit is deferred until right before the acknowledgment is sent. This is called transaction synchronization in Spring. It significantly reduces the possibility of duplicates, but they cannot be entirely eliminated.

    Comment


    • #3
      Originally posted by Gary Russell View Post
      There is always a possibility of duplication transactions unless you are using an XA transaction manager and XA-capable resources (RabbitMQ does not support XA).

      Spring AMQP implements the 'Best Efforts 1PC pattern' (http://www.javaworld.com/javaworld/j...nsactions.html).

      The DB transaction is started before the receive and the DB commit is deferred until right before the acknowledgment is sent. This is called transaction synchronization in Spring. It significantly reduces the possibility of duplicates, but they cannot be entirely eliminated.
      I have one more question, is it acceptable to use the Hibernate transaction manager to make the receiving of messages transactional? Do the commit/rollback calls executed at the SimpleMessageListenerContainer level cause any locking of the underlying DB after the call to the service commits?

      Comment


      • #4
        It does delay the commit but, in most cases, only for a very short time if exiting the @Transactional service method is at the end of the flow; just enough time to unwind the call stack back down to the listener container.

        Of course, if you have lots of work to do after you leave your @Transactional method, you might keep some resources locked for longer than before.

        The transaction is started earlier but, since there is no work done on the database, the only resource that is "locked" (reserved) is a Hibernate session (used when you eventually hit some hibernate code). There are no database resources, per se, locked until you start doing real work.

        Comment

        Working...
        X