Transaction Management In Mule ESB
What is a Transaction?
A transaction ensures that all operations in a logical sequence of the flow either completes successfully or each operation is rolled back. A transaction makes sure that all operations act as a single unit.
Mule commits the transaction if the processing of all event processors which have joined the transaction complete successfully. If any event processor raises an error then the transaction is rolled back. The transaction rollback ensures that the processing of processors is rolled back.
Transaction Types
Single Resource
A single resource transaction is one where only single resource is involved in a transaction i.e. only JMS connectors or Database connectors or VM connectors in a transaction.
A flow can consist of –
- Many Database connectors but they should all point to same database
- Many JMS connectors but they should point to same JMS Server
The connectors in the flow must use same connection configuration.
Global/XA Transactions
A Global/XA transaction is one where multiple resources are involved in a single transaction
For example:
- JMS connector and Database connector in a single transaction
- JMS connector and VM connector in a single transaction
- Multiple database connectors which are pointing to different databases
Starting Transaction
Two ways to start the transaction –
1) Event Source like VM Listener or JMS Listener will start the transaction when it is configured with the Transactional Action of “ALWAYS_BEGIN”.
2) Using Try Block and configure it with Transactional Action of “ALWAYS_BEGIN”.
Joining Transaction
Two Ways to join an existing transaction –
1) Event processor like JMS publish ,VM publish or database connector like insert,update etc, will use Transactional Action of ALWAYS_JOIN or JOIN_IF_POSSIBLE. Transaction is must for ALWAYS_JOIN and it will raise an error if the transaction is not in progress while JOIN_IF_POSSIBLE will not raise any error in the absence of a transaction. JOIN_IF_POSSIBLE will act as if the event processor is a non-transactional resource when it is not called within a transaction.
2) Try Block will use BEGIN_OR_JOIN. The block will join transaction if the transaction is already started by Event Source or it will begin a transaction if the transaction is not in progress.
Not Initiating/Ignoring /Not Supporting Transaction
Event Source
An event source will not initiate a transaction if the Transactional Action is set as NONE
Event Processor
An event processor won’t join a transaction if the Transaction Action is set as NOT_SUPPORTED. It will execute outside the existing transaction.
Try Block
Try Block won’t start or join a transaction if the Transaction Action is set as INDIFFERENT.
Configure Transaction Types
Transaction type for event source/processor or try block is defined in two ways –
LOCAL – Configure “LOCAL” as transaction type for Single Resource Transactions
XA – Configure “XA” as transaction type for Global Transactions
Example : Single Resource Transaction with Database Connectors
Let’s understand Single resource transaction with an example where Database connectors are involved. In this case, single resource transaction with database connectors is done with the use of try block.
transactionFlow –
- transactionFlow is called using HTTP listener with path /trandb?name=<name>
- variable vName is set using queryParameter – name
- This flow inserts this name into person table and insert a record into audit table
Database Config –
Database Configuration for connectors will connect to MySQL Database – muletrainingdb running in localhost
Transaction Configuration –
Transaction Configuration of Try Block –
Transaction Configuration of both INSERT database connectors –
Testing the Application –
- Start the application from Anypoint studio/on-premises server
- Test using http://localhost:8081/trandb?name=ruchi
- Check the records in both tables of the database
- Person table and Audit table –
- Test using http://localhost:8081/trandb?name=ruchisaini1
- Here length of name is greater than 10 and this will throw our demo error
- Check both tables, there will not be any record inserted
Example : Single Resource Transaction with JMS Connectors
Let’s understand Single resource transaction with an example where JMS connectors are involved.
In this case, single resource transaction is started using JMS Listener and is joined by JMS Publish.
Two flows are shown below –
senderFlow –
- senderFlow is called using HTTP listener with path /appsend1?name=<name>
- Payload is set using queryParameter – name
- This flow publishes to JMS Queue – queue1 using JMS Publish connector.
- Transaction is not used in this flow
listenerFlow –
- This flow listens on JMS Queue –queue1 using JMS Listener
- JMS Listener starts the transaction
- A JMS publish is also added in this flow which joins the transaction and sends a message to queue2
- There will be an error raised due to validation module’s event processor which checks whether length of name set in payload is greater than 10
JMS Config –
- JMS Configuration for connectors are done to connect to ActiveMQ running in localhost
Transaction Configuration –
Transaction Configuration of JMS Listener(Advanced Tab) in listenerFlow –
Transaction configuration of JMS Publish(Advanced Tab) in listenerFlow –
Testing the Application
- Start ActiveMQ by running activemq.bat from the bin folder
- Open ActiveMQ console
- Deploy and start your application. Use Anypoint studio/on-premises server.
- Try http://localhost:8081/appsend1?name=ruchi – name has size less than 10
- Notice queues on ActiveMQ Console
- The message is enqueued and dequeued from queue1 and message is enqueued in queue2 as desired
- So JMS Listener and Publish worked properly
- Lets introduce error after deleting queues from ActiveMQ Console
- Try http://localhost:8081/appsend1?name=ruchisaini1
- An error will be thrown as the length of name is greater than 10
- Check ActiveMQ console, no message goes to queue2
- The message is Enqueued and Dequeued from queue1
- JMS Listener has received message and processed it so even though an error has occurred but there was no redelivery and DLQ configured so the message is considered as Dequeued and discarded.
- In upcoming examples, we will send this message to DLQ – dead letter queue to preserve the message for the purpose of failed message reviews/use
Error Handling
On Error Continue
When we use on-error-continue in a transactional flow, the processors inside the on-error-continue runs inside the transaction and the transaction is committed.
In the above example of single resource transaction using database connectors, we have added on-error- continue. The on-error- continue has an insert database connector which inserts the errors of this flow into errors table of muletrainingdb database.
Transaction configuration of insert connector in On Error Continue is ALWAYS_JOIN.
Test the application using http://localhost:8081/trandb?name=ruchisaini1
When the error is raised, the control goes to on-error-continue and transaction that is started from try block will include the insert operation in on-error-continue . The insert operation in on-error-continue will insert the error into errors table.
As the transaction is committed so the error in the flow does not rollback the processing of preceding event processors and hence the records will also get inserted into both tables.
If the error were thrown after first insert of person table and before the insert of audit table then the effect of on-error-continue will be a transaction including insert of record in person table and errors table.
On Error Propagate
When we use on-error-propagate for handling error in a transactional flow, any processors inside the on-error-propagate is executed outside transaction.The transaction rolls back before entering the on-error-propagate.
So if we use on-error-propagate in previous example, the transaction will be rolled back. There will no record in person table and audit table. There will just be an entry of error in our errors table of muletrainingdb database.
EXAMPLE: XA Transaction with Connectors pointing to Two Different Databases
xa-transactionsFlow –
- xa-transactionsFlow is called using HTTP listener with path /xa1?myname=<name>
- variable vName is set using queryParameter – myname
- This flow inserts into tables present in two different databases
- The error response body in the responses section of http listener is set to payload and this payload is set in on error propagate
Database Config
- Database Configuration for connectors will connect to MySQL Databases – muletrainingdb database and transactiondb database running in localhost
- In the database config of both databases, click the checkbox for XA Transactions in Transactions Tab
Transaction Manager
- We will define Bitronix transaction manager
- Click Create in “Global Elements” of your integration file and select Bitronix Transaction Manager available under Component Configurations
Transaction Configuration
Try Block configuration – The try block starts the transaction using the Transactional action as “ALWAYS_BEGIN” and Transaction Type is set to XA rather than LOCAL
Database connector configuration – Set the Transaction action as ALWAYS_JOIN for both Insert connectors
Testing the Application
- Start the application from Anypoint Studio/On-Premises server
- Test using http://localhost:8081/xa1?myname=ruchi
- Check the records in both tables of different databases
- The muletrainingdb database with person table –
- The transactiondb database with person table –
- Now test using http://localhost:8081/xa1?myname=ruchisaini1
- An error will be raised as the length of myname is greater than 10
- Check in the person table of both databases, there will be no new entry corresponding to this name
- The error by us will be displayed on Browser as we have set the payload in on-error-propagate and set the error response in HTTP listener to use payload
EXAMPLE: XA Transaction with JMS Connector and Database Connector
xaflow –
- xaflow is called using HTTP listener with path /xa2?name=<name>
- variable vName is set using queryParameter – name
- This flow sends the message to ActiveMQ queue – xaq2 and inserts into person table of muletrainingdb database
JMS Config for connector
- It will connect to ActiveMQ Server running in localhost
- Click Enable XA in JMS Config
Database Config for connector
- It will connect to MySQL Databases running in localhost
- In the database config – Transaction Tab, click the checkbox for XA Transactions
Transaction Configuration
Try Block Configuration – The try block starts the transaction using the Transactional action as “ALWAYS_BEGIN” and Transaction Type is set to XA rather than LOCAL
JMS connector and Database connector : Transactional Action is configured as ALWAYS_JOIN
Testing the Application
- Start the application from Anypoint studio/on-premises server
- Test using http://localhost:8081/xa2?name=ruchi
- Check the message is sent to JMS queue and a record is inserted in the person table
- Then Test using http://localhost:8081/xa2?name=ruchisaini1
- An error will be raised as the length of name is greater than 10
- Check in the person table and the JMS queue on ActiveMQ Console, there is no entry in database and no new message in queue
Rollback and Redelivery
In case of failure, an exception is thrown which rollbacks the transaction but we need redelivery of our messages also.
The message redelivery is helpful –
- We want to preserve the event/message by sending to some other storage for the purpose of evaluating this message or because we have a critical requirement of zero message loss.
- There is a possibility that the cause of failure gets corrected in the meantime and flow processing succeeds for example – the flow lost connection to the database but then it was recovered.
Configure Redelivery for JMS – Client/Mule Side configuration
The redelivery configuration is at mule/client end so mule handles the redelivery by using a counter till it reaches max redelivery count.
- We have a JMS Listener which is listening on relQueue and a JMS Publish which sends to relQueueOut
- We will configure JMS_config of JMS Connector as shown below –
- When an exception is raised, mule redelivers the message till the time max redelivery count is reached
- In our configuration, initial redelivery delay is 2 seconds and redelivery delay between each message is 5 seconds
- In our example, the max redelivery count is 3 so mule redelivers it 3 times and when redelivery count exceeds 3 then an error – MULE:REDELIVERY_EXHAUSTED is raised
- This error is caught in on error continue with error type of MULE:REDELIVERY_EXHAUSTED
- Here, we send the message to relqueue.DLQ queue in ActiveMQ .
Configure Redelivery for JMS – Broker side configuration
Configuration is done at Broker side which is ActiveMQ in our demo case.
This configuration will be done in the activemq.xml file which is present in conf directory of ActiveMQ . Redelivery configuration is done using broker plugin and scheduler.
Following is the configuration in xml file–
Above configuration specifies redelivery count and delays. This configuration includes queue specific configuration as well as the default configuration for all other destinations.
By default non-persistent messages does not go to DLQ so we need to configure it explicitly –
More details of this configuration can be found in ActiveMQ Documentation.
Now lets see how our configuration works in the following example –
The flow has JMS Listener that is listening relQueueAQ and a JMS publish which sends the message to relQueueAQOut.
The redelivery count is 4 so the first delivery + 4 re-deliveries are made and ActiveMQ console looks like following –
5 messages Enqueued and Dequeued from my queue relQueueAQ and the message is sent to default ActiveMQ’s DLQ after re-deliveries are exhausted. No redelivery configuration is required at mule side.
Configure Redelivery for VM
- This flow has a VM Listener which listens on VM queue – queue_vm and the listener starts the transaction.
- The VM publish joins this transaction to send the message to queue_vm2.
- The redelivery policy for VM Listener is configured using redelivery tab of VM Listener as shown below –
- On Error, the event will be redelivered 4 times and each time it goes through on-error-propagate
- When number of re-deliveries are exhausted , it will call on-error-continue which is configured for the error type MULE:REDELIVERY_EXHAUSTED
- In on-error-continue, we can send it to some error VM queue or some other flow or write in some file as per our business scenario.
Reliability Pattern
In case of failures, there is a possibility of message loss but some applications have a critical requirement and require the assurance that the messages are always delivered. There must not be any message loss.
Two ways to achieve this –
Use Transactional Event Source
We can use Transactional Event source like VM Listener,JMS Listener which starts the transaction as soon as it receives Event/Message.
Use Reliability Pattern
We use a design pattern known as reliability pattern when we have a non-transaction event source like HTTP, FTP , File etc.
In case our use case requires these types of event sources then we need to use these event sources to just get the event and then send it to another flow for processing. The second flow will use Transactional Event Source and process the message so in case of failure your event is available for future use.
Flow 1 is only responsible for receiving event and Flow 2 is responsible for integration logic of processing this event. In case of failure, the transaction will roll back and the event will be redelivered few times and can be sent to some queue/file etc for handling later.
In this Article, we have understood how to use Transactions in Mule ESB. I hope you will now be able to use transactions with Mule ESB in your projects more comfortably.