Two-phase commit protocol

Welcome to the fourth post in the distributed systems series. In the last post, we covered ACID transactions. ACID transactions guarantee

  1. Atomicity: Either all the operation succeed or none
  2. Consistency: System moves from one consistent state to another at the successful completion of a transaction
  3. Isolation: Concurrent transactions do not interfere with each other
  4. Durability: After successful completion of a transaction all changes made by the transaction persist even in the case of a system failure

If the database is running on a single machine then it is comparatively easier to guarantee ACID semantics in comparison to a distributed database. Following are the reasons you would want to run a database in a distributed fashion:

  1. To be fault-tolerant
  2. To handle more reads and writes

Let’s assume our’s is a read intensive application and our single machine database is not able to scale to our demand. One of the solution to scale read is achieved through replication. The most common replication topology is single master and multiple slaves. All the writes go to the master and reads are performed on slaves. Data from master is replicated to the salves synchronously or asynchronously. In this post, we will assume synchronous replication.

Continue reading “Two-phase commit protocol”

Using MongoDB Replica Set With Spring MongoDB 1.0.0.RC1

The primary means for replication is to ensure data survives single or multiple machine failures. The more replicas you have, the more likely is your data to survive one or more hardware crashes. With three replicas, you can afford to lose two nodes and still serve the data. MongoDB supports two forms of replication, Replica Sets and Master Slave. Replica Sets is the recommended way to do replication in MongoDB and will cover only Replica Sets in this post.

Couple of weeks back I was working in POC where we need to set up MongoDB replication. As I am Spring aficionado I decided to use Spring MongoDB to interact with Replica Set. We used Spring Roo to quickly bootstrap the project. All the project setup, Spring MongoDB setup, JUnit test cases, evern Spring MVC UI was created in minutes thanks to Spring Roo. I am big Spring Roo fan — I just love it. Thanks SpringSource for such an amazing project. Spring Roo uses Spring MongoDB version 1.0.0.M5 which has a bug that it does not support WriteConcern value REPLICAS_SAFE. But with the current release 1.0.0.RC1 that issue has been fixed and now you can use REPLICAS_SAFE. REPLICAS_SAFE is the recommended value for WriteConcern in case of replication.  This is a step by step guide from creation of Spring project to working MongoDB replica set.

  1. Create the project using Spring Roo. If you are not aware of Spring Roo you can read my Spring Roo series. I am using Spring Roo to quickly configure a Spring MongoDB project.
    project --topLevelPackage com.xebia.mongodb.replication --projectName mongodb-replication-demo --java 6
    mongo setup --databaseName bookshop --host localhost --port 27017
    entity mongo --class ~.domain.Book --testAutomatically --identifierType org.bson.types.ObjectId
    field string --fieldName title --notNull
    field string --fieldName author --notNull
    field number --type double --fieldName price --notNull
    repository mongo --interface ~.repository.BookRepository --entity ~.domain.Book
    

    This will create a Spring maven project, configure MongoDB to work with Spring, create one Collection Book and will add three fields title, author, and price to the collection. All the CRUD operations will carried out using BookRepository.

  2.  Start the MongoDB server using ./mongod and run BookIntegrationTest and make sure all tests pass.
  3. Setup replica set following the MongoDB documentation http://www.mongodb.org/display/DOCS/Replica+Set+Tutorial.
  4. Update the applicationContext-mongo.xml as shown below but before add the property mongo.replicaset which will have all nodes.
    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:cloud="http://schema.cloudfoundry.org/spring" xmlns:context="http://www.springframework.org/schema/context" xmlns:mongo="http://www.springframework.org/schema/data/mongo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd        http://www.springframework.org/schema/data/mongo        http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd        http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd        http://schema.cloudfoundry.org/spring http://schema.cloudfoundry.org/spring/cloudfoundry-spring-0.8.xsd">
    
        <mongo:db-factory dbname="${mongo.database}" id="mongoDbFactory" mongo-ref="mongo"/>
    
        <mongo:repositories base-package="com.xebia.mongodb.replication"/>
    
        <!-- To translate any MongoExceptions thrown in @Repository annotated classes -->
        <context:annotation-config/>
    
        <bean class="org.springframework.data.mongodb.core.MongoTemplate" id="mongoTemplate">
            <constructor-arg ref="mongoDbFactory"/>
        </bean>
    
    	<mongo:mongo id="mongo" replica-set="${mongo.replicaset}" write-concern="REPLICA_SAFE">
    		<mongo:options auto-connect-retry="true"/>
    	</mongo:mongo>
    </beans>
    

    If you run the tests again all the tests will fail and you will see following exception.

    Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from file [/home/shekhar/dev/workspaces/writing/mongodb-replication-demo/target/classes/META-INF/spring/applicationContext-mongo.xml]; nested exception is java.lang.ArrayIndexOutOfBoundsException: 1
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.
    doLoadBeanDefinitions(XmlBeanDefinitionReader.java:412)
    

    The reason for this exception is because there is a bug in Spring MongoDB 1.0.0.M5 which is not able to parse WriteConcern REPLICA_SAFE value.

  5. To make it work we have to work with Spring MongoDB latest version 1.0.0.RC1. This is released just 3 days back on 7th December 2011.Update the pom.xml with 1.0.0.RC1.
     <dependency>
    	<groupId>org.springframework.data</groupId>
            <artifactId>spring-data-mongodb</artifactId>
            <version>1.0.0.RC1</version>
    </dependency>
    

    Run the BookIntegrationTest the tests will fail again and see the following exception stacktraces.

    java.lang.NoSuchMethodError: org.springframework.core.annotation.AnnotationUtils
    .getAnnotation(Ljava/lang/reflect/AnnotatedElement;Ljava/lang/Class;)
    Ljava/lang/annotation/Annotation;
    at org.springframework.transaction.annotation.SpringTransactionAnnotationParser
    .parseTransactionAnnotation(SpringTransactionAnnotationParser.java:38)
    
  6. To make it ran you have to use latest Spring version 3.1.0.RC2 in pom.xml
    <spring.version>3.1.0.RC2</spring.version>
    
  7. Final change you need to make is in applicationContext-mongo.xml. Change the value of write-concern to REPLICAS_SAFE.
    <mongo:mongo id="mongo" replica-set="${mongo.replicaset}" write-concern="REPLICAS_SAFE">
    	<mongo:options auto-connect-retry="true"/>
    </mongo:mongo>
    
  8. Run the tests and all the tests will pass.