ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


Using Global/Distributed Transactions in Java/JDBC with Oracle Real Application Clusters

by Sachin Shetty
12/21/2005

This document describes some of the ways to leverage the high availability features of Oracle Real Application Cluster (RAC) databases by moving the load balancing functionality from the Oracle server/Oracle driver to your application. In this article, I discuss the ways to achieve load balancing of connections across RAC nodes and yet maintain the sticky nature to the same RAC node for the all of the participants of a single global transaction. I have used Java examples wherever possible, but most of the programming constructs can be mapped to other sufficiently advanced programming languages.

An Oracle Real Application Cluster database is a clustered database. A cluster is a group of independent servers that cooperate as a single system. Clusters provide improved fault resilience and modular incremental system growth over single, symmetrical multiprocessor systems. Oracle's RAC page has more information on RAC technology.

A global transaction is a mechanism that allows a set of programming tasks, potentially using more than one resource manager and potentially executing on multiple servers, to be treated as one logical unit. Once a process is in transaction mode, any service requests made to servers may be processed on behalf of the current transaction. The services that are called and join the transaction are referred to as transaction participants. The value returned by a participant may affect the outcome of the transaction. A global transaction may be composed of several local transactions, each accessing the same resource manager. The resource manager is responsible for performing concurrency control and atomicity of updates.

Related Reading

Java Programming with Oracle JDBC
By Donald Bales

Problems with Global Transactions and Oracle RAC

Problems occur when connections participating in a distributed transaction are routed to different instances of an RAC cluster. A split transaction is a distributed transaction that spans more than one instance of an RAC database. This implies that different branches of the same distributed transaction are located on different instances of an RAC database. This could result in erroneous situations, as follows:

Recommended Approach

The following design forms the foundation of the recommended approach:

Multithreaded Application

In a multithreaded application, multiple threads are spawned to perform concurrent tasks. Usually global transactions have thread affinity; i.e., they are identified and associated by the threads in which they run. All connections participating in the same global transaction must be drawn from the pool or a data source in the same thread. You can maintain affinity to a node pool by associating the thread with the node pool. The connection factory responsible for providing the connection should then inspect the thread and reuse the node pool referenced in the thread space. The connection factory is allowed to randomly pick a pool only if there is no node pool associated with the thread. You can implement this in Java using the ThreadLocal class. When the connection factory requests a connection, it inspects the thread space to check if a node pool is associated with the thread; if there is, it reuses it. Otherwise, it randomly picks one.

/*
 * Method ConnectionFactory.getConnection
 */
Connection getConnection() {

  // nodePoolTracker is a ThreadLocal object 
  // defined in the class definition
  NodePoolIndentifier poolId = 
    (NodePoolIndentifier) nodePoolTracker.get();
  if (poolId == null) {
    // There is no node pool associated with 
    // this thread, randomly pick 
    // one and store it in the thread
    NodePool pool = getLeastLoadedInstacePool();
    nodePoolTracker.set(
      new NodePoolIdentifier(pool.getId());
    return pool.getConnection();                   
  }
  else {
    // get a reference to the pool 
    NodePool pool = getPoolById(poolId);
    return pool.getConnection();
  }
}

The above method creates an affinity between the thread and the node pool. If your application spawns a new thread for every new task, you don't need to manually clean up the thread space. However, for applications that have thread pools or reuse threads (like web servers, app servers, etc.), you need to manually clean up the thread space after each global transaction or task completion. This entirely depends on how you manage threads in your application. If you don't clean up the thread space, the node pool will get associated with the thread for the complete lifespan of the thread, which may result in suboptimal load balancing across RAC instances.

Web Application

Modern web applications have a distinct advantage over standalone applications. They run in a managed environment which delegates most of the dirty work to the underlying application/web server. Here you can leverage an approach similar to standalone applications--using a thread pool--but by using HTTP filters, you can eliminate the problem with reusable threads by resetting the state of the thread in the HTTP filters.

You can do this using Java servlets as follows:

All of the connections for a particular request are directed to a single RAC instance. Alternatively, you can also use the request object to store the reference of the node pool rather then the ThreadLocal object. The downside of this approach is that you need to pass the request object to all of the components that draw a connection to the database.

Known Issues

This approach assumes a specific pattern in the life span of global transactions that happens to work with most popular applications. Some places where this would not work include:

Resources

Sachin Shetty works as a consultant on Java and Oracle technologies.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.