Spring + JPA @Transactional + ES Listener: Throwing exception rollbacks DB but does not cancel the persistence in ES


(Diego Sosa) #1

Hello everyone,

This is not a problem related solely to ES but to how to integrate it to
Spring and JPA. The problem I am having is that I registered a few
hibernate listeners to index/deindex the DB entities into Elastic Search
once they have been persisted in the DB. The problem is that the listener
onPostInsert method is called even if I throw an exception in the method
that persists the entities and this method is marked as
@Transactional(rollbackFor = {Throwable.class}). What happens? Entity gets
into ES but DB is rollbacked.

My configuration is as follows:

*The listener class: *defines what to do whenever there is an insert,
delete, update, etc. in the database.

public class ElasticSearchEventListener implements PostDeleteEventListener,
PostInsertEventListener, PostUpdateEventListener {

@Override
public void onPostInsert(PostInsertEvent event) {
    log.debug("Listener indexing entity");
    try {
       updateElasticSearch(event.getEntity());
    } catch (Exception e) {
       log.debug("Error indexing object from listener");
       e.printStackTrace();
    }
}

.......}

*The listener configurer class: *attaches the listener class to the
appropriate Hibernate events, for example: POST_COMMIT_INSERT

@Service @Log4jpublic class ListenerConfigurerImpl implements ListenerConfigurer {
@Autowired
private EntityManagerFactory entityManagerFactory;

@Autowired
private ElasticSearchEventListener listener;

@PostConstruct @Override
public void registerListeners() {
    log.debug("Registering event listeners");
    HibernateEntityManagerFactory hibernateEntityManagerFactory = (HibernateEntityManagerFactory) this.entityManagerFactory;
    SessionFactoryImpl sessionFactoryImpl = (SessionFactoryImpl) hibernateEntityManagerFactory.getSessionFactory();
    EventListenerRegistry registry = sessionFactoryImpl.getServiceRegistry().getService(EventListenerRegistry.class);
    registry.getEventListenerGroup(EventType.POST_COMMIT_INSERT).appendListener(listener);

    .......
}}

A service class: a service method marked with @Transactional and throwing
an exception on purpose.

@Service @Log4jpublic class ConversationServiceImpl implements ConversationService {

@Override        
@Transactional(rollbackFor = {Throwable.class})
public void quotePackage(Long userId, CustomQuoteDTO dto) {
    ......
    Conversation conversation = Conversation.createAndAssign(user, agency, type, subject);
    conversation = conversationRepository.save(conversation);
    Long conversationId = conversation.getId();

    throw new RuntimeException();
}}

Based on this configuration, I would be expecting that the entity is not
saved neither in the DB nor Elastic Search. What happens is that the DB is
rollbacks correctly but the listener is called despite that. It's like it
does not know about the transaction been rollbacked. I found a bug related
to this in the hibernate forums https://hibernate.atlassian.net/browse/HHH-1582
but it's from 2006 and it is still open. Any ideas on how to solve this
problem? Another way to implement the listener may be?

Thanks in advance.

--
You received this message because you are subscribed to the Google Groups "elasticsearch" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elasticsearch+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elasticsearch/47c12659-f81a-4d49-8326-18157a5b16ed%40googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.


(Costin Leau) #2

Hi,

Guessing here but make sure your ES writing happens last during commit and not post insert. The difference between the
two is that the former means no exceptions have been triggered and the tx is about to be committed, the latter that the
insert is successful but the tx is on-going and it can still be rolled back.

Looking at your code, you marked ES to be occur after an insert - so the behavior that you are seeing is correct.
Considering using a Hibernate interceptor or even better, Spring's TransactionSynchronization#afterCommit.
It all depends on how easy you can pass the contextual object between your app and the post commit hook...

On 14/01/2014 6:27 PM, Agustin Lopez wrote:

Hello everyone,

This is not a problem related solely to ES but to how to integrate it to Spring and JPA. The problem I am having is that
I registered a few hibernate listeners to index/deindex the DB entities into Elastic Search once they have been
persisted in the DB. The problem is that the listener onPostInsert method is called even if I throw an exception in the
method that persists the entities and this method is marked as @Transactional(rollbackFor = {Throwable.class}). What
happens? Entity gets into ES but DB is rollbacked.

My configuration is as follows:

*The listener class: *defines what to do whenever there is an insert, delete, update, etc. in the database.

|public class ElasticSearchEventListener implements PostDeleteEventListener,
PostInsertEventListener, PostUpdateEventListener {

 @Override
 public  void  onPostInsert(PostInsertEvent  event)  {
     log.debug("Listener indexing entity");
     try  {
        updateElasticSearch(event.getEntity());
     }  catch  (Exception  e)  {
        log.debug("Error indexing object from listener");
        e.printStackTrace();
     }
 }

 .......

}|

*The listener configurer class: *attaches the listener class to the appropriate Hibernate events, for example:
POST_COMMIT_INSERT

|@Service @Log4j
public class ListenerConfigurerImpl implements ListenerConfigurer {
@Autowired
private EntityManagerFactory entityManagerFactory;

 @Autowired
 private  ElasticSearchEventListener  listener;

 @PostConstruct  @Override
 public  void  registerListeners()  {
     log.debug("Registering event listeners");
     HibernateEntityManagerFactory  hibernateEntityManagerFactory=  (HibernateEntityManagerFactory)  this.entityManagerFactory;
     SessionFactoryImpl  sessionFactoryImpl=  (SessionFactoryImpl)  hibernateEntityManagerFactory.getSessionFactory();
     EventListenerRegistry  registry=  sessionFactoryImpl.getServiceRegistry().getService(EventListenerRegistry.class);
     registry.getEventListenerGroup(EventType.POST_COMMIT_INSERT).appendListener(listener);

     .......
 }

}|

*A service class:*a service method marked with @Transactional and throwing an exception on purpose.

|@Service @Log4j
public class ConversationServiceImpl implements ConversationService {

 @Override
 @Transactional(rollbackFor=  {Throwable.class})
 public  void  quotePackage(Long  userId,  CustomQuoteDTO  dto)  {
     ......
     Conversation  conversation=  Conversation.createAndAssign(user,  agency,  type,  subject);
     conversation=  conversationRepository.save(conversation);
     Long  conversationId=  conversation.getId();

     throw  new  RuntimeException();
 }

}|

Based on this configuration, I would be expecting that the entity is not saved neither in the DB nor Elastic Search.
What happens is that the DB is rollbacks correctly but the listener is called despite that. It's like it does not know
about the transaction been rollbacked. I found a bug related to this in the hibernate forums
https://hibernate.atlassian.net/browse/HHH-1582 but it's from 2006 and it is still open.Any ideas on how to solve this
problem? Another way to implement the listener may be?

Thanks in advance.

--
You received this message because you are subscribed to the Google Groups "elasticsearch" group.
To unsubscribe from this group and stop receiving emails from it, send an email to
elasticsearch+unsubscribe@googlegroups.com.
To view this discussion on the web visit
https://groups.google.com/d/msgid/elasticsearch/47c12659-f81a-4d49-8326-18157a5b16ed%40googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

--
Costin

--
You received this message because you are subscribed to the Google Groups "elasticsearch" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elasticsearch+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elasticsearch/52D56D72.6070006%40gmail.com.
For more options, visit https://groups.google.com/groups/opt_out.


(system) #3