Is there any transaction isolation level available in JMS queue like in JDBC ?
More specifically in transitional mode, when a client consumes a message and has not committed yet, will the next client consume the next message? If the 1st client rolls back transition, where the message placed in the queue, I think it will be placed where it was earlier.
Short answer: no.
JMS sending is either transactional or not, which means the message is "committed" (visible for the broker and MDBs) when senders transaction commits or immediately (even if client sending transaction rollbacks later).
JMS receiving has simple on/off transactions as well: message is marked as received either when it's picked up by MDB (non-transactional) or when MDB returns without exception (transactional).
Transaction isolation is about visibility of DB table changes. JMS messages are immutable, you send it once and you cannot change it.
Related
Let's consider the following context:
2 spring integration channels, they are each in separate database transactions. At the end of the first transaction, a message is put into the second channel. In the first channel, elements are created in the database that is later consumed by the corresponding message that has been sent from the first channel to the second channel.
To make sure that the transaction from channel 1 is fully committed before the second channel is triggered our subclass of the JpaTransactionManager is registering a TransactionSynchronization in the prepareForCommit method it overrides from the JpaTransactionManager
The flow (channel 1) looks like this:
Do all the message processing and database handling
Last step of the flow registers a TransactionSynchronization that does a MessageChannel.send in the afterCommit phase to send the message to channel 2
My understanding is that at the time the message is sent to the second channel (in afterCommit) all changes that have been done in the database transaction of channel 1 are flushed and committed.
Now the second channel does some work (like an MQ PUT) and later updates an entry that was created in the first flow. We have now observed that the repository method returned no entry in the database, but it is visible in the table at a later point. Other entries that were also created in the transaction of the first channel however are visible. This happens only once every few thousand messages, normally they are there but sometimes they are not visible for the second channel a few milliseconds after the transaction has been committed by channel 1.
I have created an image that should illustrate it:
The Chain 1 is the first chain that consists of multiple ServiceActivators that perform database work, a splitter that generates more messages and then another ServiceActivator that I named SENDER which registers the TransactionSynchronization that (so my understanding) should send the for example 3 generated messages to chain 2 after the red transaction is fully committed and therefore before the blue transaction begins.
One thing I have noticed is that the entries that were sometimes present and sometimes not are all in the one method that (not on purpose) uses javax.transaction.Transactional instead of org.springframework.transaction.annotation.Transactional. However, we are using spring core 5.0.8.RELEASE and in other questions I have seen that this should make 0 difference since spring 4.2.x.
I don't think the afterCommit is the right place to send messages downstream.
There should be just enough to have a Service Activator for POJO method marked with the #Transactional. This way a transaction is going to start and finish exactly around this method call. The result of the method is going to be sent to the output channel already, exactly after that transaction is committed.
UPDATE
The best way to achieve your requirements is a <gateway> around your Chain1. This way the TX is going to be committed over there before producing reply to the Chain2 from the gateway.
With the TransactionSynchronization::afterCommit there is no guarantee that TX is going to be committed on DB when QueueChannel is ready for polling messages. Although you can use JdbcChannelMessageStore for transactional storage of messages. This way they are not going to be visible until TX commit in DB.
See more about <gateway> in Docs: https://docs.spring.io/spring-integration/docs/current/reference/html/messaging-routing-chapter.html#_calling_a_chain_from_within_a_chain
JMS Session interface has two similar methods: rollback and recover. They are used in different ack mode: rollback in transaction and recover in client ack. But except that, what is the main difference of these two methods.
rollback: tells the broker that current client will not process current message(s). Broker policy then dictates if it will redeliver to the same (or other) consumer or roll the messages to a dead letter queue
recover: instructs the broker to redeliver all unacknowledged messages to the current session
I'm new to JMS and would appreciate your help.
Everything I read about JMS gives me the impression that the object that "holds a transaction" is a session object, meaning that when we "session.commit()", everything that has happened since last commit is settled.
My question regards, how to handle the patter where I want to "transact messages", not the session. Imagine that you want to receive a large number of trades in a very concurrent scenario, and upon receiving any individual message and processing you'd like to "message.commit()". How can we implement this scenario ?
Would I have to keep creating QueueSessions for every message that I wanted to dequeue ? Isn't there too much overhead ?
Thanks in advance
It is correct, a JMS Session object is responsible for managing transactions. The Session.Commit() call commits all messages received/sent in that session. Similarly the Session.Rollback() rolls back messages in a session.
For your requirement you can use a non-transacted session with CLIENT_ACKNOWLEDGE option. Like:
createSession(false, Session.CLIENT_ACKNOWLEDGE);
A CLIENT_ACKNOWLEDGE session let's you do a message.Acknowledge which tells the messaging provider to remove that message from queue/topic.
But you have to keep in mind that the implementation of message.Acknowledge is implementation dependent. Some messaging providers allow per message acknowledge where as others do a acknowledge all messages. In per message acknowledge, only the message on which acknowledge is called, will be removed from messaging provider. All other messages received and not acknowledged will not be removed. Where as in the 'acknowledge all' type of implementation, calling acknowledge on one message will acknowledge all messages received before. This is same as doing a session.commit in a transacted session.
I am going to use a Session to commit the read of a JMS message after it (and any corresponding write) has successfully completed.
However, if I have an error, and have to do a rollback, I would like to process new messages first, rather than the one that had (caused???) the error that had to be rolled back. I want to eventually reprocess the failed message, but not to fail over and over while other yet-unseen messages stall behind it, waiting for action to remove the offending message or fix the environment that made it fail.
Is this automatic? (will be using Sonic MQ, if that matters). If so, the rest of this question is moot.
Do I need to, or can I even, reset the priority of the failed message to push it further back in the queue (behind other pending messages, if any)? If I need to reset the priority, how do I make that "stick", given that I would have rolled back the transaction that initially read the message in question.
I am not aware of feature in Sonic MQ which supports your requirements out-of-the-box, but there are other options:
Use a second queue for failed messages. So failed messages are sent again on another queue. Processing could start, if the first queue is empty, for example.
Resend the message on the same queue (with same or even with a lower priority)
In both cases, after the message has been sent, there is a normal commit on the main queue.
Related: Message processing with priorities. A quote from James Shek's answer:
JMS Spec states: "JMS does not require that a provider strictly implement priority ordering of messages; however, it should do its best to deliver expedited messages ahead of normal messages."
I am working on a multithreaded JMS receiver and publisher code.
XML message is received from a Queue, stored procedures(takes 70 sec to execute) are called and response is send to Topic within 90 sec.
I need to handle a condition when broker is down.
i.e. a condition in which messages are received from Queue and are being processed in java, in the mean time both Queue and Topic will be down. Then how to handle those messages which are not on queue and not send to topic but are in java memory?
Different options available:
1.To use CLIENT_ACKNOWLEDGE
2.To separate publisher code from receiver code.
3.To have error utility which will take messages from log and process them and send to Topic(least preferred)
Please suggest me the right option
Use a transacted session. Consume the message and send the response under a single unit of work and explicitly call COMMIT after sending the response. Then if the broker dies while the transaction is outstanding the input message will be rolled back. If you include the DB update in a two-phase coordinated transaction then it too can be rolled back of the broker goes down. This requires the consumer and responder to be within the same thread because JMS scopes sessions per thread, but you can have several threads running sessions in parallel.
Be aware that keeping many transactions open for 90 seconds might require some tuning at the broker side.
One solution is to use db for intermediate stored procedure. So the steps would be:
1. Consume Message from Queue and store into db and use Client_Acknowledge.
2. Run stored procedures on the consumed message.
3. Once the procedure is over, send message from db to the topic.
4. Delete message once acknowledgement is recieved.
If queue and topic goes down in between, you just need to send message again when acknowledgement is not recieved from topic.
Well I am not sure whether this is the best alternative and want to see how community responds on this question.