JBoss.orgCommunity Documentation

Chapter 15. TorqueBox Distributed Transactions

15.1. Overview
15.2. The TorqueBox.transaction method
15.3. Messaging
15.4. Database Configuration
15.4.1. PostgreSQL
15.4.2. MySQL
15.4.3. Example

TorqueBox takes advantage of its host's robust transactional facilities. JBoss provides state-of-the-art distributed XA transaction support, and TorqueBox exposes this to Ruby developers in a concise, often transparent API.

It's important to understand the difference between a conventional database transaction and a distributed transaction: multiple resources may participate in a distributed transaction. The most common example of a transactional resource is a relational database, but other examples include message brokers and some NoSQL data grids. Distributed transactions allow your application to say, tie the success of a database update to the delivery of a message, i.e. the message is only sent if the database update succeeds, and vice versa. If either fails, both rollback.

In addition, Rails ActiveRecord models are enhanced when run in TorqueBox so that connections from multiple, class-specific databases can indeed participate in a single distributed transaction. Further, the behavior of nested transaction rollbacks won't surprise you: if the child rolls back, the parent will, too, excepting when the :requires_new option is passed to the child. Callbacks for after_commit and after_rollback work as one would expect.

You may explicitly demarcate a transaction using TorqueBox.transaction. If the block of commands you pass to it runs to completion without raising an exception, the transaction is committed. Otherwise, it is rolled back. It's just that simple. It accepts the following arguments:



Obviously, the above example is contrived. When multiple transactional components collaborate, you don't often know how methods, invoked directly or indirectly, might demarcate their transactions. Rarely would you explicitly nest transactions within one method, but the above serves as an example showing the effects of transaction scope.

The above example also shows the options for setting scope either as a symbol, e.g. :requires_new, or a hash, e.g. :scope => :requires_new. The deprecated syntax, :requires_new => true, matching the Rails convention, is provided for backwards compatibility.

By default, MessageProcessors are not transactional. To make them transactional, add xa: true to the processor's entry in torquebox.yml or torquebox.rb - see Section 8.4.8.3, “Connecting Consumers to Destinations” for more information.

After you've enabled transactions for the message processor, each on_message(msg) invocation demarcates a transaction. If no exceptions are raised, the transaction commits. Otherwise, it rolls back. This is the default behavior and requires no additional configuration on your part.

Any messages published to any JMS destinations automatically become part of the current transaction, by default. So they won't be delivered until that transaction commits.

Any manipulations of your Rails ActiveRecord models (persisted to your XA-compliant database) within on_message(msg) will become part of its transaction if distributed transactions have been enabled for that database.

Occasionally, you may not want a published message to assume the active transaction. In that case, pass :tx => false, and the message will be delivered whether the active transaction commits or not. This option works for backgrounded tasks as well.

Backgroundable tasks are not transactional.

Ensure your application is correctly configured to use the activerecord-jdbc-adapter and then add xa: true to your database.yml entry for each database you want to use in a distributed transaction. See Chapter 14, Database Connectivity in TorqueBox for more details on configuring ActiveRecord JDBC support.

Distributed transactions are restricted to those databases supported by both the activerecord-jdbc-adapter and JBoss XA datasources. Currently, that includes PostgreSQL, MySQL, H2, Derby, Oracle, Microsoft SQL Server, and IBM DB2. Sqlite3 doesn't support XA. Default installations of some of these databases may require additional configuration to support XA.