CDI event observer handling on server crash and re

2019-03-03 09:29发布

问题:

I am trying to persist an item in DB using a service and firing a JMS message for the subsequent service to pick up the persisted item so that it can process the same. This particular operations happens in a single transaction. But due to race condition at times, the second service is not able to fetch the respective item as its not yet persisted.

My use case is very common and there are some many discussion related to this in various forums. One solution for this issue is to use CDI events. I tried the same and could solve a part of the problem. The pseudo code is as follows:

@Inject
@Transaction
private Event<Item> itemEvent;

public void handleItem(Item item) {
  //code to persist the item
  itemEvent.fire(item);
}

@Asynchronous
public void observeAfterTransactionCompletion(@Observes(during = TransactionPhase.AFTER_SUCCESS) @Transaction Item item) {
  //code to send JMS message to the second service
}

My only issue is when the persistence is successful & event is fired and just before the observer starts processing the event if the Jboss server goes down, the second service will not be notified as the JMS message wont be send.

Will the CDI container can internally handle this scenario so that the event will always be invoked? Will the observer be notified on server start automatically? If not, How do I handle this scenario so that my approach is fool proof?

NOTE: I have already tried out the retry approach in second service till the message is available. Also persisting the event in an other queue is an alternate approach which seems very tedious. Looking for a smart approach.

UPDATE: My initial code was written in such a way that the persistence and messaging was in single transaction. But that resulted in message consumption by second service before even first service persistence is successful resulting in error as second service could not find the required data which is yet to be persisted.

UPDATE 2: Intial approach pseudo code as follows: @TransactionAttribute(TransactionAttributeType.REQUIRED) public void processMessage() { // code to persist data to DB // code to publish JMS message to the consumer }

Even by using xa-datasouce and xa-connection factory, the issue still exists.

回答1:

One approach is to use XA transactions.

Both JMS and database resources join the same transaction, so the JMS message is fired on the commit, either an error in database update or in JMS message sending will trigger the rollback of the whole operation.

Note that you'll have to change the datasource definition do use xa-datasource, update the JMS connection factory configuration to use xa and that the JMS session must be transacted as well

 <!-- JMS connection factory configuration -->
 <pooled-connection-factory name="myCxFactory">
     <transaction mode="xa"/>
     [...]
 </pooled-connection-factory>

// JMS session creation (message producer)
Session session = connection.createSession(true, Session.SESSION_TRANSACTED);

Note also that only the message production is part of the transaction, not his consumption