How to Run a Geode Cache Transaction

This topic describes how to run a Geode cache transaction.

Applications manage transactions on a per-cache basis. A Geode cache transaction starts with a CacheTransactionManager.begin method and continues with a series of operations, which are typically region operations such as region create, update, clear and destroy. The begin, commit, and rollback are directly controlled by the application. A commit, failed commit, or voluntary rollback by the transaction manager ends the transaction.

You can run transactions on any type of cache region except regions with global scope. An operation attempted on a region with global scope throws an UnsupportedOperationException exception.

A transaction may not be nested within another transaction. An attempt to begin a nested transaction will throw an IllegalStateException exception.

This discussion centers on transactions on replicated and partitioned regions. If you use non-replicated distributed regions, follow the guidelines for replicated regions.

  1. Configure the cache copy-on-read behavior in the members hosting the transactional data, or perform cache updates that avoid in-place changes. This allows the transaction manager to control when cache updates are visible outside the transaction. See Setting Global Copy on Read.
  2. Configure your regions for transactions in the members hosting the transactional data.

    If you use… then you should…
    replicated regions Use distributed-ack scope. The region shortcuts specifying REPLICATE use distributed-ack scope. This is particularly important if you have more than one data producer. With one data producer, you can safely use distributed-no-ack.
    partitioned regions Custom partition and colocate data among regions so all the data for any single transaction is hosted by a single member. If the transaction is run from a member other than the one hosting the data, the transaction will run by proxy in the member hosting the data. The partitioned region must be defined for the application that runs the transaction, but the data can be hosted in a remote member.
    persistent regions Configure Geode to allow transactions on persistent regions. By default, the configuration does not allow transactions on persistent regions. Enable the use of transactions on persistent regions by setting the property gemfire.ALLOW_PERSISTENT_TRANSACTIONS to true.
    a mix of partitioned and replicated regions Make sure any replicated region involved in the transaction is hosted on every member that hosts the partitioned region data. All data for a single transaction must reside within a single host.
    delta propagation Set the region attribute cloning-enabled to true. This lets Geode do conflict checks at commit time. Without this, the transaction will throw an UnsupportedOperationInTransactionException exception.
    global JTA transactions with only Geode cache transactions Set the region attribute ignore-jta to true for all regions that you do not want to participate in JTA global transactions. It is false by default. For instructions on how to run a JTA global transaction, see JTA Global Transactions with Geode.
  3. Update your cache event handler and transaction event handler implementations to handle your transactions. Cache event handlers may be used with transactions. Cache listeners are called after the commit, instead of after each cache operation, and the cache listeners receive conflated transaction events. Cache writers and loaders are called as usual, at the time of the operation.

    Follow these additional guidelines when writing cache event handler callbacks:

    • Make sure cache callbacks are transactionally aware, because a transactional operation could launch callbacks that are not transactional.
    • Make sure cache listeners will operate properly, given entry event conflation. Two events for the same key are conflated by removing the existing event and queuing the new event.

    See Using Cache Writer and Cache Listener Plug-Ins for more information.

    Transaction event handlers are available. Transaction event handlers are cache-wide. You can install one transaction writer and any number of transaction listeners. Follow these guidelines:

    • Implement with synchronization for thread safety. Listener and writer handlers may be invoked at the same time by different threads for different transactions.
    • Keep transactional callback implementations lightweight, and avoid doing anything that might cause the callbacks to block.
    See Configuring Transaction Plug-In Event Handlers for more information.

  4. Write the transaction code. For example:

    CacheTransactionManager txManager =
    try {
        // ... do work
    } catch (CommitConflictException conflict) {
        // ... do necessary work for a transaction that failed on commit

    Follow these guidelines when writing the transaction:

    • Start each transaction with a begin operation.
    • Consider whether you will want to suspend and resume the transaction. If some operations should not be part of the transaction, you may want to suspend the transaction while performing non-transactional operations. After the non-transactional operations are complete, you can resume the transaction. See Basic Suspend and Resume Transaction Example for an example.
    • If your transaction operates on a mix of partitioned and replicated regions, do the first region operation on an entry of the partitioned region. This sets the host for the entire transaction.
    • If you did not configure copy-on-read to true, be sure all cache updates avoid in-place changes.
    • Take into account the behavior of transactional and non-transactional operations. All transactional operations that are run after the begin and before the commit or rollback are included in the transaction.
    • End each transaction with a commit or a rollback. Do not leave any transaction in an uncommitted or unrolled back state. Transactions do not time out, so they will remain for the life of the application.
  5. Review all of your code for compatibility with transactions. When you commit a transaction, while the commit is in process, the changes are visible in the distributed cache. This provides better performance than locking everything involved with the transaction updates, but it means that another process accessing data used in the transaction might get some data in the pre-transaction state and some in the post-transaction state.

    For example, suppose keys 1 and 2 are modified within a transaction, such that both values change from A to B. In another thread, it is possible to read key 1 with value B and key 2 with value A, after the commit begins, but before the commit completes. This is possible due to the nature of Geode reads. This choice sacrifices atomic visibility in favor of performance; reads do not block writes, and writes do not block reads.