When Flux begins executing the first action in a flow, it starts a transaction. Flux will continue to execute successive actions in the same transaction until it reaches a transition point called a transaction break. When the engine reaches a transaction break, it commits all of the workflow data (including variables and action results) to the database. The next action will then begin a new transaction and Flux repeats this process until the workflow is complete.
By default, every trigger is a transaction break. Flux will not hold a transaction open while a trigger is waiting for an event to occur. This prevents the engine from unnecessarily holding open long transactions when there is no work to do, and allows other work (another workflow, for example) to run while the trigger is waiting.
In addition to triggers, the Null Action and Join are marked as transaction breaks by default. On triggers and these two actions, the transaction break setting cannot be overridden.
Aside from triggers, the Null Action, and the Join, any other action can be configured to be (or not be) a transaction break. This is done simply by setting the transaction break property of the action to true.
One advantage of transactions breaks is that they provide a point to restart if an error occurs during a workflow’s execution. For example, consider a workflow with three Java Actions running in sequence. By default, all of the Java Actions will run in a single transaction - if an error occurs, therefore (an action throws an exception, the database goes down, etc.), this transaction will be rolled back to its starting point and the entire workflow will begin executing again.
If each Java Action is marked as a transaction break, though, Flux will only roll back to the last executing action that crashed.
Transactions are committed at the start of the action that is marked as a transaction break. This means that all non-transient data coming into the action (like runtime data maps, flow context variables, action results) will be persisted in the database. In the event of a rollback, the last action that was marked as a transaction break will be the first action to begin executing once the rollback is complete.
When setting transaction breaks, you should strongly consider the benefit you will gain from using a transaction break. Too many transaction breaks can cause performance to suffer (since Flux must commit a significant amount of data at each transaction break); on the other hand, you may have strict requirements about which actions can re-execute after they have successfully completed once. Setting transaction breaks in your workflow requires carefully balancing performance considerations against your own requirements.
If the Flux engine is using a data source, and a workflow invokes an EJB or publishes a JMS message on an application server, that workflow’s transaction will be conducted within the scope of a user transaction. If Flux and your EJB are both using the same data source, they will both be integrated into the same transaction. If Flux is configured to use direct JDBC database connections instead of a data source, the workflow and EJB will not use the same transaction.
You can also choose to separate the Flux workflow transaction from your EJB transaction by having the EJB’s transaction attribute set to RequiresNew.
If Flux uses a different data source than your EJB, you can still integrate your workflow transaction with the EJB transaction by using XA transactions. See XA Transactions below for more information on this.
XA transactions allow Flux to be integrated with your application server transactions across multiple data sources. To use XA transactions, Flux must be configured with a data source that has XA transactions enabled.
To configure Flux to use XA transactions, enable the DATA_SOURCE_USER_TRANSACTIONS property in your engine configuration:
If your application server does not accept the standard java:comp/UserTransaction JNDI name for looking up user transaction objects, you can change the JNDI lookup name that Flux will use. You can configure client- and server-side connections to use different user transaction JNDI names as well.
The JNDI names for user transactions are set in the engine configuration like so:
Note that when you make client calls into the Flux engine, those transactions are seamless by default, and XA transactions do not need to be configured, because the transaction originates in the application server, not the Flux engine.
For clarity, JSE triggers and actions execute in the same transaction as the Flux engine itself. Consequently, should a system crash occur, the JSE actions will re-execute and eventually commit the transaction. There is no requirement to configure XA transactions when your workflows do not invoke EJBs or publish JMS messages. Even in this latter case, the decision to configure XA transactions is up to you.
One final note: there is at least one additional case where it makes sense to use XA transactions even though your workflows do not call EJBs or publish JMS messages. Suppose you are using a workflow that consists of a simple timer trigger, which flows into a Java action. As part of your Java action listener code, you may want to update a separate database from the database that the Flux engine uses. In this case, it is not sufficient to use the database connection provided to you by the FlowContext and KeyFlowContext objects. This database connection, which is used by the Flux engine, cannot access other databases.
To bridge this gap, you can use XA transactions. Configure the Flux engine to use XA transactions. When your workflow fires and your Java action listener code is invoked, write your listener code so that it accesses a second database by looking up a second XA data source from your application server. The database connection obtained from this second XA data source must be governed by the JTA user transaction that is already in progress. Using this technique, a workflow can update data directly in separate databases within a single transaction.
When you call methods on the Flux engine from a JSE application (outside a JEE application server), each method normally runs within its own transaction and commits at the end of the method. However, if you wish to call multiple methods on the Flux engine all within a single transaction, you must employ the usual JTA and javax.transaction.UserTransaction techniques.
When you call methods on the Flux engine from a transactional EJB (inside a JEE application server), by default, all the method calls on the Flux engine occur within the scope of a single user transaction, emanating from inside the application server.
engine.put(workflow1); engine.put(workflow2); engine.put(workflow3);
Using the code above, either all three workflows are added to the Flux engine, or none are. There is no need to use a UserTransaction object. However, you must have initialized the Flux engine with your application server’s database connection pool, using a data source or an XA data source.
If you call the above methods on a Flux engine from a non-transactional client, for example, a Servlet or an EJB with the SUPPORTS transaction attribute then the Flux engine will detect no existing transaction and will start a transaction for each of the above method calls. In short, the three method calls will be in their own separate transaction.
You can choose to start a global transaction in your non-transactional client by looking up a user transaction object in your application server and calling UserTransaction.start() on it. After doing this all your Flux method calls will be in the same transaction until you call UserTransaction.commit() or UserTransaction.rollback(). The Flux engine will relinquish control of the transaction to the client call.
The section above applies to using Flux with XA data sources as well. The Flux engine is capable of detecting an existing JEE user transaction and participating in it.