How to Run a GemFire Cache Transaction

How to Run a GemFire Cache Transaction

This topic describes how to run a GemFire cache transaction.

Applications manage transactions on a per-cache basis. A GemFire cache transaction starts with a CacheTransactionManager.begin method and continues with a series of operations, which are typically region operations such as region create, 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. A transaction attempted on these regions throws an UnsupportedOperationException.

Before you begin, have your members and regions defined where you want to run transactions.

Note: The discussions on transactions in this guide use 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 your cache updates are visible outside the transaction.
  2. Configure your regions for transactions in the members hosting the transactional data. Depending on the type of regions and features you are using, you should configure your regions differently for transactions.
    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 between regions so all the data for any single transaction is hosted by a single data store. If you run the transaction from another member, it 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 GemFire to allow transactions on peristent regions. By default, GemFire does not allow transactions on persistent regions. You can enable the use of transactions on persistent regions by setting the gemfire property gemfire.ALLOW_PERSISTENT_TRANSACTIONS to true.
    a mix of partitioned and replicated regions Make sure your replicated regions are hosted in every member that hosts the partitioned region data. All data for a single transaction must reside in a single host.
    delta propagation Set the region attribute cloning-enabled to true. This lets GemFire do conflict checks at commit. Without this, when you run the transaction, you'll get this exception:
    ("Delta without cloning cannot be used in transaction");
    global JTA transactions (but you only want to run a GemFire cache transaction) 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 GemFire.
  3. Update your cache event handler and transaction event handler implementations to handle your transactions.

    Cache event handlers are all used for transactions. Cache listeners are called after the commit, instead of after each cache operation, and they receive the conflated transaction events. Cache writers and loaders are called as usual, as the operations are done.

    Follow these additional guidelines when writing your cache event handler callbacks:
    • Make sure cache callbacks are transactionally aware. A transactional operation can launch callbacks that are not transactional.
    • Make sure cache listeners will operate properly with the entry event conflation done for transactional operations. Two entry events for the same key are conflated by removing the existing event and adding the new event to the end of the list.

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

    If desired, you can also configure transaction event handlers. Transaction event handlers are cache-wide. You can install one transaction writer and any number of transaction listeners. Use the following guidelines:
    • Program with synchronization for thread-safety. Your listener and writer methods may be called simultaneously by different threads for different transactions.
    • Keep your transactional callback implementations lightweight and avoid doing anything that might cause them to block.

    See Configuring Transaction Plug-In Event Handlers for more information.

  4. Using the CacheTransactionManager, write your transaction. For example:
    CacheTransactionManager txManager =
    try {
    	// ... do work
         } catch (CommitConflictException conflict)
    Follow these guidelines when writing your transaction:
    • Start each transaction with a begin operation.
    • Run the GemFire function and other operations that you want included within the transaction.
    • 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 runs on a mix of partitioned and replicated regions, perform your first transaction operation on a partitioned region. This sets the host for the entire transaction.
    • If you did not configure copy-on-read to true, be sure your 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 and unrolled back state. Transactions do not time out, so they will remain in the system for the life of your application.
  5. Review all of your code for compatibility with transactions.

    When you commit a transaction, while the commit is taking place, 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 key 1 and 2 are written to in a transaction so both of their 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, while the transaction is being committed. This is possible due to the nature of GemFire reads. This choice sacrifices atomic visibility in favor of performance; reads do not block writes, and writes do not block reads.