<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="transactions">
  <title>TorqueBox Distributed Transactions</title>

  <section>
    <title>Overview</title>
    <para>
      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.
    </para>
    <para>
      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.
    </para>

    <para>
      In addition, Rails <classname>ActiveRecord</classname> 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
      <code>:requires_new</code> option is passed to the child.
      Callbacks for <code>after_commit</code> and
      <code>after_rollback</code> work as one would expect.
    </para>

  </section>

  <section>
    <title>The <code>TorqueBox.transaction</code> method</title>
    <para>
      You may explicitly demarcate a transaction using
      <code>TorqueBox.transaction</code>.  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:

      <itemizedlist>
        <listitem>
          <para>
            An arbitrary number of XAResources to enlist in the
            current transaction. This is rarely needed since TorqueBox
            message destinations, background tasks and caches are all
            transactionally aware. They will enlist themselves in the
            transaction defined by <code>TorqueBox.transaction</code>
            automatically, by default.
          </para>
        </listitem>

        <listitem>
          <para>
            Optionally, either a symbol or a hash indicating the scope
            of the transaction. The <code>:scope</code> attribute
            provides analogs to <ulink
            url="http://docs.oracle.com/javaee/6/tutorial/doc/bncij.html">the
            JEE transaction attributes</ulink>. The default is
            <code>:required</code>.
          </para>
        </listitem>

        <listitem>
          <para>A block defining your transaction. All actions taken
          in the block will be committed after the block is called
          unless an exception is raised, in which case the transaction
          will be rolled back.</para>
        </listitem>
      </itemizedlist>

      <table>
        <title>Transaction Scopes</title>

        <tgroup cols="2">
          <colspec align="left" />

          <thead>
            <row>
              <entry>Scope</entry>

              <entry>Description</entry>
            </row>
          </thead>

          <tbody>
            <row>
              <entry><parameter>:required</parameter></entry>

              <entry>Execute within current transaction, if any,
              otherwise start a new one, execute, commit or
              rollback.</entry>
            </row>

            <row>
              <entry><parameter>:requires_new</parameter></entry>

              <entry>Suspend current transaction, if any, start a new
              one, execute, commit or rollback, and resume the
              suspended one.</entry>
            </row>

            <row>
              <entry><parameter>:not_supported</parameter></entry>

              <entry>Suspend current transaction, if any, and execute
              without a transaction. Also,
              <parameter>:none</parameter> is an alias.</entry>
            </row>

            <row>
              <entry><parameter>:supports</parameter></entry>

              <entry>Execute the body whether there's a transaction or
              not; may lead to unpredictable results</entry>
            </row>

            <row>
              <entry><parameter>:mandatory</parameter></entry>

              <entry>Toss an exception if there's no active
              transaction</entry>
            </row>

            <row>
              <entry><parameter>:never</parameter></entry>

              <entry>Toss an exception if there is an active
              transaction</entry>
            </row>

          </tbody>
        </tgroup>
      </table>

    </para>

    <para><example>
      <title>Nesting transactions with different scopes</title>

      <para><programlisting>TorqueBox.transaction do
  # ... tx #1 created due to default :required scope
  TorqueBox.transaction(:none) do
    # ... tx #1 suspended
    # exceptions raised here won't rollback actions in this block
    TorqueBox.transaction(:scope => :requires_new) do
      # ... tx #2 begun
      # exceptions raised here will rollback tx #2
    end
    # ... tx #2 committed
  end
  # ... tx #1 resumed
  TorqueBox.transaction(:mandatory) do
    # ... actions become a part of tx #1
  end
  TorqueBox.transaction(:requires_new => true) do  # deprecated syntax
    # ... tx #1 suspended and tx #3 begun
  end
  # ... tx #3 committed (or rolled back) and tx #1 resumed
  # exceptions raised (or uncaught) here will rollback tx #1
end
# ... tx #1 committed
      </programlisting></para>
    </example></para>

    <para>
      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.
    </para>

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

  </section>

  <section id="transaction-messaging">
    <title>Messaging</title>
    <para>
      By default, all <classname>MessageProcessors</classname> are
      transactional, so each <methodname>on_message(msg)</methodname>
      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.
    </para>
    <para>
      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.
    </para>
    <para>
      All <classname>Backgroundable</classname> tasks are
      transactional, so if invoked within a transaction, it will only
      start when the transaction commits.
    </para>
    <para>
      Any manipulations of your Rails
      <classname>ActiveRecord</classname> models (persisted to your
      XA-compliant database) within
      <methodname>on_message(msg)</methodname> will become part of its
      transaction.
    </para>
    <para>
      Occasionally, you may not want a published message to assume the
      active transaction. In that case, pass <code>:tx =>
      false</code>, and the message will be delivered whether the
      active transaction commits or not.  This option works for
      backgrounded tasks as well.
    </para>
  </section>

  <section id="transaction-configuration">
    <title>Configuration</title>
    <para>
      No extra configuration of your app is required other that what
      you'd normally do for a database-aware Ruby application,
      i.e. standard configuration of the
      <code>activerecord-jdbc-adapter</code>.  See <xref
      linkend="database"/> for more details.
    </para>
    <para>
      Distributed transactions are restricted to those databases
      supported by both the <code>activerecord-jdbc-adapter</code> 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.
    </para>

    <section>
      <title>PostgreSQL</title>
      <para>
        To enable full distributed transaction support in PostgreSQL,
        you'll need to set <code>max_prepared_transactions</code> to
        something greater than zero in
        <filename>postgresql.conf</filename>, which is the usual
        default in most installations.  Changing it requires a server
        restart.
      </para>
    </section>

    <section>
      <title>MySQL</title>
      <para>
        To achieve transactional support -- even non-distributed
        functionality -- you must enable the <code>InnoDB</code>
        storage engine. As of MySQL 5.5, this is the default storage
        engine.
      </para>
    </section>

  </section>

  <section>
    <title>Examples</title>

    <para>Distributed transactions support comes enabled by default,
    so no extra configuration is required to enable it. If you don't
    need this feature, it is possible to disable it by setting
    <code>xa</code> to false in your
    <filename>database.yml</filename>. If your application uses more
    than one database, you'll need to enable distributed transactions
    explicitly for the additional databases by setting <code>xa</code>
    to true.</para>

    <para><example>
      <title>Disabling XA support
      (<filename>config/database.yml</filename>)</title>

      <para><programlisting>production:
  adapter: mysql
  xa: false
  database: my_database
  host: my_host
  username: my_username
  password: my_password
  encoding: utf8
      </programlisting></para>
    </example></para>

    <para><example>
      <title>Enabling XA support for additional databases
      (<filename>config/database.yml</filename>)</title>

      <para><programlisting>some_other_database:
  adapter: mysql
  xa: true
  database: my_database
  host: my_host
  username: my_username
  password: my_password
  encoding: utf8
      </programlisting></para>
    </example></para>
  </section>
</chapter>
