How to configure a JDBC Store for Transactions

This article discusses how to configure the JTA (Java Transaction API) Transaction Log Store in WildFly. We will learn how to configure a DataSource based JDBC Store to persist the transaction data.

In a distributed transaction, multiple resources (such as databases, message queues, or file systems) are involved in a single transaction. The transaction manager is responsible for coordinating the commit or rollback of the transaction across all the resources involved.

To ensure the integrity of the transaction, the transaction manager logs the transaction data in a transaction log store. If a failure occurs and the transaction cannot be completed, the transaction manager can use the log store to recover the transaction and ensure that the resources involved are left in a consistent state.

The default configuration of WildFly uses a File System Log store to persist the transaction Data:

<subsystem xmlns="urn:jboss:domain:transactions:6.0">
    <!-- ... -->
    <object-store path="tx-object-store" relative-to="jboss.server.data.dir"/>
</subsystem>>

Using a File System Store for transactions is a good solution in a scenario where you have a stable File System architecture. On the other hand, you may be running on an ephemeral storage. This is common in some Cloud architectures. In this case, you need to move to a more stable location for your Transaction Logs

Configuring the JDBC Store in WIldFly

To configure WildFly to use a JDBC store for transactions, you will need to perform the following steps:

  1. Install a JDBC driver for your database. You will need to download and install a JDBC driver that is compatible with your database and WildFly.
  2. Create a data source in WildFly. See as an example: How to configure a Datasource with JBoss / WildFly
  3. Configure the transaction manager to use the JDBC store. In the standalone.xml configuration file, add a transaction-jdbc-store element to the transactions subsystem configuration. Set the data-source attribute to the JNDI name of the data source that you created in step 2.

Assumed that you have already completed the steps 1 and 2, you can configure the JDBC Transaction Store as follows:

# We need to set the JTA Attribute to false for the Datasource
 /subsystem=datasources/data-source=PostgrePool:write-attribute(name=jta,value=false)
# Then we set the JDBC DataSource for the Store
 /subsystem=transactions:write-attribute(name=jdbc-store-datasource, value=java:/PostGreDS)
# We enable the JDBC Store
 /subsystem=transactions:write-attribute(name=use-jdbc-store, value=true)

The above commands assume that you have a Datasource which is bound at the JNDI Address: java:/PostGreDS

Here is an example of what the transactions subsystem configuration might look like with a JDBC store configured:

<subsystem xmlns="urn:jboss:domain:transactions:6.0">
    <core-environment node-identifier="${jboss.tx.node.id:1}">
        <process-id>
            <uuid/>
        </process-id>
    </core-environment>
    <recovery-environment socket-binding="txn-recovery-environment" status-socket-binding="txn-status-manager"/>
    <coordinator-environment statistics-enabled="${wildfly.transactions.statistics-enabled:${wildfly.statistics-enabled:false}}"/>
    <object-store path="tx-object-store" relative-to="jboss.server.data.dir"/>
    <jdbc-store datasource-jndi-name="java:/PostGreDS"/>
</subsystem>

You need to restart WildFly for changes to take effect. Which tables narayana uses to store the Log ?

By logging into the Database shell we can have a look at the default Table where Narayana writes the logs:

Configuring the JDBC Store in WIldFly

As you can see, the Transaction Table is “jbosststxtable”. We can gather more details about this table as follows:

postgres=# \d jbosststxtable;
                     Table "public.jbosststxtable"
   Column    |          Type          | Collation | Nullable | Default 
-------------+------------------------+-----------+----------+---------
 statetype   | integer                |           | not null | 
 hidden      | integer                |           | not null | 
 typename    | character varying(255) |           | not null | 
 uidstring   | character varying(255) |           | not null | 
 objectstate | bytea                  |           |          | 

Configuring JDBC Store programmatically

You can also configure the Transaction Store to use a JDBC Resource programmatically. To do that, you can use the com.arjuna.ats.arjuna.common.MetaObjectStoreEnvironmentBean which is part of the Narayana transaction manager API.

This class allows configuring the properties of the object store, for example the database to store the transaction logs for distributed transactions.

    public static void setupStoreViaConfigBean() {
        final String jdbcStoreClass = JDBCStore.class.getName();
        final String jdbcAccess = DynamicDataSourceJDBCAccess.class.getName();
        final String DB_CLASSNAME = "org.h2.jdbcx.JdbcDataSource";
        final String DB_URL = "jdbc:h2:file:./target/h2/JBTMDB";
        final String dataSourceJndiName = String.format("%s;ClassName=%s;URL=%s;User=sa;Password=sa",
                jdbcAccess, DB_CLASSNAME, DB_URL);

        final MetaObjectStoreEnvironmentBean metaObjectStoreEnvironmentBean =
                BeanPopulator.getDefaultInstance(MetaObjectStoreEnvironmentBean.class);

        // set the store type to the JDBC store
        metaObjectStoreEnvironmentBean.setObjectStoreType(jdbcStoreClass);
        // set the JNDI name of the datasource to be  used to access the database
        metaObjectStoreEnvironmentBean.setJdbcAccess(dataSourceJndiName);
    }
Found the article helpful? if so please follow us on Socials