Tuesday, March 3, 2015

Spring Distributed transactions using Best Effort 1 Phase Commit



We all know that we commonly use the Java Transaction API and the XA protocol for distributed transactions in Spring. We have some other options as well  whose implementation depends on the types of resources our application uses and the trade-offs we're willing to make between performance, safety, reliability, and data integrity.
We have different options to maintain distributed transaction like 2-phase commit with XA protocol but there are some performance trade-offs as well with those transaction management. Here we will discuss about Best Effort 1PC which is very effective Distributed Transaction Management techmnique.

Introduction-
The basic idea in this technique  is to delay the commit of all resources as late as possible in a transaction so that the only thing that can go wrong is an infrastructure failure (not a business-processing error). It  is often good enough if the participants are aware of the compromises. Many high-volume, high-throughput transaction-processing systems are set up this way to improve performance.
Systems that rely on Best Efforts 1PC reason that infrastructure failures are rare enough that they can afford to take the risk in return for higher throughput. If business-processing services are also designed to be idempotent, then little can go wrong in practice.The Best Efforts 1PC pattern is fairly general but can fail in some circumstances that the developer must be aware of. This is a non-XA pattern that involves a synchronized single-phase commit of a number of resources. Because the 2PC is not used, it can never be as safe as an XA transaction,
To understand this concept we will go with an example –
For our example we have two different database transaction resources. So in example we will update two different databases. One database transaction is started before the another database one, and they end (either commit or rollback) in reverse order. So the sequence in the case is:


Here committing of resources should be in order, why the ordering is important is technical, but the order itself is determined by business requirements. The order tells you that one of the transactional resources in this case is special; it contains instructions about how to carry out the work on the other. This is a business ordering: the system cannot tell automatically which way round it is (although if messaging and database are the two resources then it is often in this order). The reason the ordering is important has to do with the failure cases. The most common failure case (by far) is a failure of the business processing (bad data, programming error, and so on). In this case both transactions can easily be rigged to respond to an exception and rollback. In that case the integrity of the business data is preserved, with the timeline being similar to the ideal failure case .

Here in our example we are using following :
1.      Maven
2.      Spring 3.0
3.      Hibernate 3.0
4.      MySQL
In our example we will achieve Best Effort 1PC using Chaining of Transaction Manager in Spring.
Our Project package structure is shown below:




Pom.xml for this project is



<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>com.test</groupId>
      <artifactId>BestEffort1PC</artifactId>
      <packaging>war</packaging>
      <version>0.0.1-SNAPSHOT</version>
      <name>BestEffort1PC Maven Webapp</name>
      <url>http://maven.apache.org</url>
      <dependencies>
            <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-core</artifactId>
                  <version>3.1.2.RELEASE</version>
            </dependency>
            <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-orm</artifactId>
                  <version>3.1.2.RELEASE</version>
            </dependency>
            <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-jms</artifactId>
                  <version>3.1.2.RELEASE</version>
            </dependency>
                                <dependency>
                  <groupId>commons-httpclient</groupId>
                  <artifactId>commons-httpclient</artifactId>
                  <version>3.1</version>
            </dependency>

            <dependency>
                  <groupId>com.thoughtworks.xstream</groupId>
                  <artifactId>xstream</artifactId>
                  <version>1.4.7</version>
            </dependency>
            <dependency>
                  <groupId>org.hibernate</groupId>
                  <artifactId>hibernate-core</artifactId>
                  <version>3.6.10.Final</version>
            </dependency>
            <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.9</version>
        </dependency>
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.4</version>
        </dependency>
       
        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.17.1-GA</version>
        </dependency>
           
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-commons</artifactId>
            <version>1.8.2.RELEASE</version>
        </dependency>          
      </dependencies>
      <build>
            <finalName>BestEffort1PC</finalName>
      </build>
</project>

 

Here we have two database :
1.      Order
2.      ProcessOrder
We will prepare order and store it in Order database and then insert this into ProcessOrder database after successful processing the status of Order will be completed.


You can see we have defined two sessionfactories for two different databases
1       hibernate3AnnotatedSessionFactory and
2           Phibernate3AnnotatedSessionFactory

 <bean id="hibernate3AnnotatedSessionFactory"
        class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="packagesToScan">
            <list>
                <value>com.test.model</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.current_session_context_class">thread</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
    </bean>
    
    
    <bean id="hibernate3AnnotatedPSessionFactory"
        class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="pdataSource" />
        <property name="packagesToScan">
            <list>
                <value>com.test.ordprocessmodel</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <!--  <prop key="hibernate.current_session_context_class">thread</prop>-->
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
    </bean> 

 As we configure our database using hibernate annotated session factory now its time to define DAO classes and Service classes. We will define two different dao classes for two different databases. Here are those classes:
1.      OrderDao.java and its implementation OrderDaoImpl.java and,
2.      POrderDao.java and its implementation POrderDaoImpl.java
Also, there is one service class ProductService.java which will add an order in Order database and also add it in ProcessOrder database and finally update the order with Success. 
 You can see all classes in sample project.

So in our ProcessService class we have processOrder method with annotation "@Transactional" which will make this method to be part of transaction.

After defining Daos and Service here comes our application context in which we will define our Chained Transaction manager to do our Magic !!! . Our application will define two different transaction managers otransactionManager and ptransactionManager for different databases. We will be using org.springframework.data.transaction.ChainedTransactionManager and pass all transactionmanagers which we need to be involved in one transaction.
This ChainedTransactionManager will delay the commit of all transaction managers until any exception occurred or all things will work successfully.

<bean id="otransactionManager"
          class="org.springframework.orm.hibernate3.HibernateTransactionManager"
          p:sessionFactory-ref="hibernate3AnnotatedSessionFactory" />
    <bean id="ptransactionManager"
          class="org.springframework.orm.hibernate3.HibernateTransactionManager"
          p:sessionFactory-ref="hibernate3AnnotatedPSessionFactory" />
    <bean id="myProductServiceTarget" class="com.test.service.ProductService"/>
   
   
    <bean id="transactionManager" class="org.springframework.data.transaction.ChainedTransactionManager">
        <constructor-arg>
            <list>
                <ref bean="otransactionManager"/>
                <ref bean="ptransactionManager"/>
            </list>
        </constructor-arg>
    </bean>


Now its time to run TestChainedTransaction.java which will throw RuntimeException and rollback all DB operations.



The precise mechanism for triggering the rollback is unimportant; several are available. The important point is that the commit or rollback happens in the reverse order of the business ordering in the resources. In the sample application, the Second Databse  must commit last because the instructions for the business process are contained in that resource. This is important because of the (rare) failure case in which the first commit succeeds and the second one fails. Because by design all business processing has already completed at this point, the only cause for such a partial failure would be an infrastructural problem with the Database Connection.




Important Points:
1. The important point here is that this technique is very efficient and is good in performance when using multiple resources. 
2.  Also this technique can be used where we do not have XA drivers for transactional resources for    Phase Commit.
3.  It is not effective in infrastructure failures which are very rare. Hence application which can survive these types of issues will be very effective with Best Effort 1 PC


You can download the sample project on below link:
https://drive.google.com/file/d/0BwvELMMMQIaDa2ZZRE1jYUxRdHc/view?usp=sharing

2 comments:

  1. A true friend is one who overlooks your failures and tolerates your success! See the link below for more info.


    #failures
    www.ufgop.org

    ReplyDelete
  2. I really enjoyed reading your article. I found this as an informative and interesting post, so i think it is very useful and knowledgeable. I would like to thank you for the effort you have made in writing this article.


    edupdf.org

    ReplyDelete