0
votes

I have a stateless EJB which inserts data into database, sends a response immediately and in the last step calls an asynchronous EJB. Asynchronous EJB can run for long (I mean 5-10 mins which is longer then JPA transaction timeout). The asynchronous ejb needs to read (and work on it) the same record tree (only read) as the one persisted by stateless EJB.

Is seems that the asynchronous bean tries to read the record tree before it was commited or inserted (JPA) by the statelsss EJB so record tree is not visible by async bean.

Stateless EJB:

@Stateless
public class ReceiverBean {

    public void receiverOfIncomingRequest(data) {
        long id = persistRequest(data);
        sendResponseToJmsBasedOnIncomingData(data);
        processorAsyncBean.calculate(id);
        }
    }
}

Asynchronous EJB:

@Stateless
public class ProcessorAsyncBean {

    @Asynchronous
    public void calculate(id) {
        Data data = dao.getById(id); <- DATA IS ALLWAYS NULL HERE!

        // the following method going to send
        // data to external system via internet (TCP/IP)
        Result result = doSomethingForLongWithData(data);

        updateData(id, result);
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void updateData(id, result) {
        dao.update(id, result);
}

Maybe I can use a JMS queue to send a signal with ID to the processor bean instead of calling asyc ejb (and message driven bean read data from database) but I want to avoid that if possible.

Another solution can be to pass the whole record tree as a detached JPA object to the processor async EJB instead of reading data back from database.

Can I make async EJB work well in this structure somehow?

-- UPDATE --

I was thinking about using Weblogic JMS. There is another issue here. In case of big load, when there are 100 000 or more data in queue (that will be normal) and there is no internet connection then all of my data in the queue will fail. In case of that exception (or any) appears during sending data via internet (by doSomethingForLongWithData method) the data will be rollbacked to the original queue based on the redelivery-limit and repetitaion settings of Weblogic. This rollback event will generate 100 000 or more threads on Weblogic in the managed server to manage redelivery. That new tons of background processes can kill or at least slow down the server.

I can use IBM MQ as well because we have MQ infrastructure. MQ does not have this kind of affect on Weblogic server but MQ does not have redelivery-limit and delay function. So in case of error (rollback) the message will appear immediately on the MQ again, without delay and I built a hand mill. Thread.sleep() in the catch condition is not a solution in EE application I guess...

2

2 Answers

2
votes

Is seems that the asynchronous bean tries to read the record tree before it was commited or inserted (JPA) by the statelsss EJB so record tree is not visible by async bean.

This is expected behavior with bean managed transactions. Your are starting the asynchronous EJB from the EJB with its own transaction context. The asynchronous EJB never uses the callers transaction context (see EJB spec 4.5.3). As long as you are not using transaction isolation level "read uncommited" with your persistence, you won't see the still not commited data from the caller.

You must think about the case, when the asynch job won't commit (e.g. applicationserver shutdown or abnormal abortion). Is the following calculation and update critical? Is the asynchronous process recoverable if not executed successfully or not even called?

You can think about using bean managed transactions, commiting before calling the asynchronous EJB. Or you can delegate the data update to another EJB with a new transactin context. This will be commited before the call of the asynchronous EJB. This is usally ok for uncritical stuff, missing or failing.

Using persistent and transactional JMS messages along with a dead letter queue has the advantage of a reliable processing of your caclulation and update, even with stopping / starting application server in between or with temporal errors during processing.

0
votes

You just need to call async method next to the one with transaction markup, so when transaction is committed.

For example, caller of receiverOfIncomingRequest() method, could add

processorAsyncBean.calculate(id);

call next to it.

UPDATE : extended example

CallerMDB

@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void onMessage(Message message) {
    long id = receiverBean.receiverOfIncomingRequest(data);
    processorAsyncBean.calculate(id);
}

ReceiverBean

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public long receiverOfIncomingRequest(data) {
    long id = persistRequest(data);
    sendResponseToJmsBasedOnIncomingData(data);
    return id;        
}