Monday, 6 January 2014

Map Reduce Counters

Counters
One of the feature provided by Map Reduce Framework is Counters

Counters helps in gathering statistics about the job. These statistics are useful for quality control and problem diagnosis

Built-in Counters:
For every job, hadoop maintains some built-in counters which report various metrics
e.g There are counters for the number of bytes and records processed, which allows you to confirm that the expected amount of input was consumed and the expected amount of output was produced.

There are different built in counters related to Job , FileSystem 

Counters are global:
Counters are maintained by the task with which they are associated, and periodically sent to the tasktracker and then to the jobtracker, so they can be globally aggregated.The built-in Job Counters are actually maintained by the jobtracker, so they don’t need to be sent across the network, unlike all other counters, including user-defined ones.
Counter values are definitive only once a job has successfully completed.

Custom Counters:
MapReduce allows users to define a set of counters, which are incremented as desired in the mapper or reducer. Counters are defined by a Java enum. A job may define an arbitrary number of enums, each with an arbitrary number of fields. The name of the enum is the group name, and the enum’s fields are the counter names.  
Counters are global: the MapReduce framework aggregates them across all maps and reduces to produce a grand total at the end of the job.

Declare Counter

enum CustomCounters {
        VALID,
        INVALID ,
        SUM
}

This Counter hold three fields :
Valid gives total count of valid records
Invalid gives total count of valid records
Sum gives the sum of 2nd column

Increment the value of Counter:
context.getCounter(CustomCounters.VALID).increment(1);

Retrieve the value of Counter :
long sum = context.getCounter(CustomCounters.SUM).getValue();

Complete Code: 

public class MR_CounterDemo {
// Declaring a Counter
   enum CustomCounters {
         VALID,
         INVALID ,
         SUM
    }

public static class FilterMapper extends Mapper<Object, Text, IntWritable,LongWritable>{


public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
   if(value!=null){
        String[] line = value.toString().split(",");
        if(line.length==3){
 // Increment the counter
            context.getCounter(CustomCounters.VALID).increment(1);
            int field = Integer.parseInt(line[1]);
 // Retreive the counter
            context.getCounter(CustomCounters.SUM).increment(field);
            long sum = context.getCounter(CustomCounters.SUM).getValue();
            int keyOut = Integer.parseInt(line[0]);
            context.write(new IntWritable(keyOut),new LongWritable(sum));
       }
     }
     else{
         context.getCounter(CustomCounters.INVALID).increment(1);
       }
   }
}

public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = new Job(conf, "Map Reduce Counter Usage");
job.setJarByClass(MR_CounterDemo.class);
job.setMapperClass(FilterMapper.class);
job.setMapOutputKeyClass(IntWritable.class);
job.setMapOutputValueClass(LongWritable.class);
job.setOutputKeyClass(LongWritable.class);
job.setOutputValueClass(Text.class);
FileInputFormat.addInputPath(job, new Path("/path/to/inputfile"));
FileOutputFormat.setOutputPath(job, new Path("/path/to/outputfile"));
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}


Counter are accessible in map and reduce method 
In its output ,you see below lines:
13/12/20 14:32:54 INFO mapred.JobClient: Job complete: job_local_0001
13/12/20 14:32:54 INFO mapred.JobClient: Counters: 23
13/12/20 14:32:54 INFO mapred.JobClient: wordcount.newapi.MR_CounterDemo$CustomCounters
13/12/20 14:32:54 INFO mapred.JobClient: SUM=87
13/12/20 14:32:54 INFO mapred.JobClient: INVALID=4
13/12/20 14:32:54 INFO mapred.JobClient: VALID=7
Above lines shows the counter values for custom counter fields
Buit in counters:
3/12/20 14:32:54 INFO mapred.JobClient: File Output Format Counters
13/12/20 14:32:54 INFO mapred.JobClient: Bytes Written=47
13/12/20 14:32:54 INFO mapred.JobClient: FileSystemCounters
13/12/20 14:32:54 INFO mapred.JobClient: FILE_BYTES_READ=576
13/12/20 14:32:54 INFO mapred.JobClient: FILE_BYTES_WRITTEN=65031
..........................................................
Above lines shows the counter values for built -in counter fields

To access in-built counters associated with job, you can try below code:
Counters counters = job.getCounters();
long counter = counters.findCounter("org.apache.hadoop.mapred.Task$Counter", "MAP_INPUT_RECORDS").getValue();

counters variable will hold following values:

FileSystemCounters
        FILE_BYTES_READ=198258
        FILE_BYTES_WRITTEN=219848
    Map-Reduce Framework
        Combine input records=0
        Combine output records=0
        Total committed heap usage (bytes)=519438336
        CPU time spent (ms)=0
        Map input records=258
        Map output bytes=141
        Map output materialized bytes=150
        Map output records=1
        Physical memory (bytes) snapshot=0
        Reduce input groups=1
        Reduce input records=1
        Reduce output records=0
        Reduce shuffle bytes=0
        Spilled Records=2
        SPLIT_RAW_BYTES=141
        Virtual memory (bytes) snapshot=0
    File Input Format Counters
        Bytes Read=8937
    File Output Format Counters
        Bytes Written=0

These counters can be accessed by using its property name.

Tuesday, 10 December 2013

DataStax Hybrid Cluster SetUp

This post will set up a hybrid cluster of datastax. Hybrid means one node is for cassandra and another is for solr.

Here i will show the configuration for one cassandra and one solr node .... But in the same way you can add the configuration for 'N' number of nodes

DSE-3.1.0 Multi node Hybrid cluster setup:

Here, we will set up a two node cluster in which first node will be cassandra node and second will be of solr node.

Lets us say, node which will be of cassandra node has ip - ip1 and node of solr node has ip - ip2

Cassandra node : ip1
Solr node : ip2

Prerequisites:

DSE-3.1.0 tar
Download the Tar:
1. Dse tar can be downloaded from : http://downloads.datastax.com/enterprise/dse-3.1.0-bin.tar.gz
Or you can use wget command to download this:
wget http://<user_name>:<password>@downloads.datastax.com/enterprise/dse-3.1.0-bin.tar.gz
For this you must be registered with this site.

Configuration Steps:
1.      Place the tar in same locations on all nodes in the cluster.
            Location in this cluster : /home/softwares/ dse-3.1.0
2.      Extract the dse-3.1.0-bin.tar.gz on all nodes.

 Configuration matrix:
 For cassandra node ip1

File : cassandra.yaml
Location of File: DSE{installation directory/resources/cassandra/conf}    
Property/Value:

num_tokens                           1
initial_token                          -9223372036854775808


data_file_directories             Path/to/dseinstallation/resources/                                                                           cassandra/tmp/var/lib/cassandra/data

commitlog_directory            Path/to/dseinstallation/resources/
                                              cassandra/tmp/var/lib/cassandra/commitlog

saved_caches_directory       Path/to/dseinstallation/resources/
                                              cassandra/tmp/var/lib/cassandra/saved_caches

seed_provider                           ip1
listen_address                           ip1
rpc_address                              ip1
read_request_timeout_in_ms    50000
range_request_timeout_in_ms   50000
write_request_timeout_in_ms    50000
request_timeout_in_ms              50000

File: log4j-server.properties
Location Of File:   DSE{installation directory/resources/cassandra/conf}
Property/Value:

log4j.appender.R.File:            Path/to/dseinstallation/resources/
                                               cassandra/tmp/var/log/cassandra/system.log

log4j.appender.V.File             Path/to/dseinstallation/resources/
                                               cassandra/tmp/var/log/cassandra/solrvalidation.log

For Solr node ip2

File : cassandra.yaml
Location of File: DSE{installation directory/resources/cassandra/conf}    
Property/Value:

num_tokens                           1
initial_token                          -6148914691236517206

data_file_directories             Path/to/dseinstallation/resources/                                                                         cassandra/tmp/var/lib/cassandra/data

commitlog_directory             Path/to/dseinstallation/resources/
                                              cassandra/tmp/var/lib/cassandra/commitlog

saved_caches_directory        Path/to/dseinstallation/resources/
                                              cassandra/tmp/var/lib/cassandra/saved_caches

seed_provider                           ip1
listen_address                           ip2
rpc_address                              ip2
read_request_timeout_in_ms    50000
range_request_timeout_in_ms   50000
write_request_timeout_in_ms    50000
request_timeout_in_ms              50000

File: log4j-server.properties
Location Of File:   DSE{installation directory/resources/cassandra/conf}
Property/Value:

log4j.appender.R.File:           Path/to/dseinstallation/resources/
                                               cassandra/tmp/var/log/cassandra/system.log

log4j.appender.V.File             Path/to/dseinstallation/resources/
                                               cassandra/tmp/var/log/cassandra/solrvalidation.log


Note:
·        Path pointing to the following properties should pre-exist
                                    data_file_directories
                                    commitlog_directory
                                    saved_caches_directory

·        It is good to mention the log directory to check where all the logs will be created. As mentioned in the above log4j-server.properties file. Path should pre-exist also

·       Token Generation Utility  To calculate tokens use below command:

python -c 'print [str(((2**64 / number_of_tokens) * i) - 2**63) for i in range(number_of_tokens)]'

For example, to generate tokens for 6 nodes:

python -c 'print [str(((2**64 / 6) * i) - 2**63) for i in range(6)]'


['-9223372036854775808', '-6148914691236517206', '-3074457345618258604', '-2',

 '3074457345618258600', '6148914691236517202']

It displays the token for each node

Now update the generated token value in initial_token property in cassandra.yaml

Start the cluster:
Start the cassandra node on ip1       Path/to/dseinstallation/bin/dse cassandra

Start Solr on ip2                              Path/to/dseinstallation/bin/dse cassandra -s

Check that your cluster is up and running:

            Packaged installs: Path/to/dseinstallation/bin/nodetool status

Now you can access solr server at http://ip2:8983/solr/#/

Thursday, 29 August 2013

Cassandra Data Model

My previous posts related to cassandra gives an overview of what is cassandra and how to install cassandra

This posts will describe how to insert and fetch data from cassandra database:

Cassandra Keyspace and Column Family : Cassandra keyspace is sort of like a relational database. It defines one or more column families, which are very roughly analogous to tables in the relational world.it’s enough to think of a column family as a multidimensional ordered map that you don’t have to define further ahead of time. Column families hold columns, and columns are the atomic unit of data storage.

Keyspaces :A cluster is a container for keyspaces—typically a single keyspace. A keyspace is the outermost container for data in Cassandra, corresponding closely to a relational database. Like a relational database, a keyspace has a name and a set of attributes that define keyspace-wide behavior.
To my knowledge, there are currently no naming conventions in Cassandra for such items.

Column families :
In the same way that a relational database is a container for tables, a keyspace is a container for a list of one or more column families. A column family is roughly analagous to a table in the relational model, and is a container for a collection of rows. Each row contains ordered columns. Column families represent the structure of your data. Each keyspace has at least one and often many column families.

Cassandra is considered schema-free because although the column families are defined, the columns are not. You can freely add any column to any column family at any time, depending on your needs.

Cassandra provides two interface to interact with it.
  • Cassandra-cli
  • cassandra cql
Cassandra cql provides sql like interface to cassandra tables

Enter in cassandra cli:Run the following command to connect to your local Cassandra instance:
bin/cassandra-cli

You should see the following message, if successful:
Connected to: "Test Cluster" on 127.0.0.1/9160
Welcome to Cassandra CLI version 1.0.7
Type 'help;' or '?' for help.
Type 'quit;' or 'exit;' to quit.
[default@unknown]
You can access to the online help with 'help;' command.

Note:Commands are terminated with a semicolon (';') in the cli.

Some basic commands to be run via cassandra-cli:

To see the name of the current cluster you’re working in, type:

[default@unknown] show cluster name
Test Cluster

To see which keyspaces are available in the cluster, issue this command:
[default@unknown] show keyspaces
system

If you have created any of your own keyspaces, they will be shown as well
The system keyspace is used internally by Cassandra, and isn’t for us to put data into. In this way, it’s similar to the master and temp databases in Microsoft SQL Server. This keyspace contains the schema definitions and is aware of any modifications to the schema made at runtime. It can propagate any changes made in one node to the rest of the cluster based on timestamps.


Create keyspace and column family via cli
create keyspace demo with placement_strategy = 'org.apache.cassandra.locator.SimpleStrategy' and strategy_options = {replication_factor:1};

CREATE COLUMN FAMILY users
WITH comparator = UTF8Type
AND key_validation_class=UTF8Type
AND column_metadata = [
{column_name: full_name, validation_class: UTF8Type}
{column_name: email, validation_class: UTF8Type}
{column_name: state, validation_class: UTF8Type}
{column_name: gender, validation_class: UTF8Type}
{column_name: birth_year, validation_class: LongType}
];

Inserting Data in column family:
[default@demo] SET users['testuser']['full_name']='Sachin';
[default@demo] SET users['testuser']['email']='sachtechie@gmail.com';
[default@demo] SET users['testuser']['state']='TX';
[default@demo] SET users['testuser']['gender']='M';
[default@demo] SET users['testuser']['birth_year']='1995';

Secondary index on column:
The CLI can be used to create secondary indexes (indexes on column values). You can add a secondary index when you create a column family or add it later using the UPDATE COLUMN FAMILY command.
e.g: to add a secondary index to the birth_year column of the users column family:

[default@demo] UPDATE COLUMN FAMILY users
WITH comparator = UTF8Type
AND column_metadata = [{column_name: birth_year, validation_class: LongType, index_type: KEYS}];

Get the record from table:
Because of the secondary index created for the column birth_year, its values can be queried directly for users born in a given year as follows:

[default@demo] GET users WHERE birth_year = 1969;

Delete a row or column:
For example, to delete the state column for the testuser row key in the users column family:
[default@demo] DEL users ['testuser']['state'];
[default@demo] GET users ['testuser'];
Or to delete an entire row:
[default@demo] DEL users ['testuser'];

cassandra cql:

In CQL 3, identifiers, such as keyspace and table names, are case-insensitive unless enclosed in double quotation marks. You can force the case by using double quotation marks.

Enter in cql
./cqlsh --cql3

Create keyspace and column family
CREATE KEYSPACE demo WITH strategy_class = 'SimpleStrategy' AND strategy_options:replication_factor='1';

create table children ( childId varchar, firstName varchar, lastName varchar, country varchar, state varchar, zip varchar, primary key (childId ) ) ;

insert into children (childId, firstName, lastName, country, state, zip) values ('sachin.arora', 'sachin', 'arora', 'India', 'Delhi', 'EI33'); 
insert into children (childId, firstName, lastName, country, state, zip) values ('owen.oneill', 'Owen', 'O''Neill', 'IRL', 'D', 'EI33');
insert into children (childId, firstName, lastName, country, state, zip) values ('collin.oneill', 'Collin', 'O''Neill', 'IRL', 'D', 'EI33');
insert into children (childId, firstName, lastName, country, state, zip) values ('richie.rich', 'Richie', 'Rich', 'USA', 'CA', '94333');
insert into children (childId, firstName, lastName, country, state, zip) values ('johny.b.good', 'Johny', 'Good', 'USA', 'CA', '94333');
insert into children (childId, firstName, lastName, country, state, zip) values ('bart.simpson', 'Bart', 'Simpson', 'USA', 'CA', '94111');
insert into children (childId, firstName, lastName, country, state, zip) values ('dennis.menace', 'Dennis', 'Menace', 'USA', 'CA', '94222');
insert into children (childId, firstName, lastName, country, state, zip) values ('michael.myers', 'Michael', 'Myers', 'USA', 'PA', '18964'); 

Misc Queries:
cqlsh:demo> SELECT * FROM children ;
cqlsh:demo> select * FROM children WHERE childid='sachin.arora';
cqlsh:demo> create index country_index on children (country) ;
cqlsh:demo> select * FROM children WHERE childid='sachin.arora' and country='India';
cqlsh:demo> SELECT count(*) from children ;
cqlsh:demo> select * FROM children WHERE childid='sachin.arora' and country='India' and state='Delhi' Allow Filtering;
cqlsh:demo> SELECT * FROM  children WHERE childid in('sachin.arora','owen.oneill') Allow filtering;


With this basic set of queries we are good to explore nosql cassandra  database.

Separate table directories
Internally cassandra creates separate directories for keyspaces and column families. Casandra stores table to disk using separate table directories within each keyspace directory.
Data files are stored using this directory and file naming format:

/var/lib/cassandra/data/ks1/cf1/ks1-cf1-hc-1-Data.db

The new file name format includes the keyspace name to distinguish which keyspace and table the file contains when streaming or bulk loading data. Cassandra creates a subdirectory for each table, which allows you to symlink a table to a chosen physical drive or data volume.

Cassandra also provides thrift,hector,astyananx and many more APIs to interact with it

Wednesday, 28 August 2013

Sonar Set Up


Sonar is an open source web-based application to manage code quality which covers seven axes of code quality as: 
  • Architecture and design
  • comments
  • duplications
  • unit tests
  • complexity
  • potential bugs and coding rules. 
Sonar is Developed in Java. It can cover projects in Java, Flex, PHP, PL/SQL, Cobol and Visual Basic 6. 
This post will cover sonar installation and how to use it:

Sonar Installation:

1. Download sonar server from http://dist.sonar.codehaus.org/.  Let us install sonar of 2.11 version
sonar-2.11

Running sonar server with default derby database:
Start the server :<path/to sonar/installtion/directory/>/bin/linux-x86-32 : ./sonar.sh start
eg.: /home/impadmin/sws/sonar-2.11/bin/linux-x86-32 : ./sonar.sh start
By default this server starts on 9000 port.Verify the same on UI : http://localhost:9000 

Running sonar server using mysql database:
In mysql prompt, fire below queries:
CREATE DATABASE sonar CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE USER 'sonar' IDENTIFIED BY 'sonar';
GRANT ALL ON sonar.* TO 'sonar'@'%' IDENTIFIED BY 'sonar';
GRANT ALL ON sonar.* TO 'sonar'@'localhost' IDENTIFIED BY 'sonar';
FLUSH PRIVILEGES; 

Once it is done edit sonar.properties file:
Path of file: <path/to sonar/installtion/directory>/conf/sonar.properties
eg: /home/impadmin/sws/sonar-2.11/conf/sonar.properties

Comment the properties related to derby;
#sonar.jdbc.url: jdbc:derby://localhost:1527/sonar;create=true
#sonar.jdbc.driverClassName: org.apache.derby.jdbc.ClientDriver
#sonar.jdbc.validationQuery: values(1)

Uncomment the properties related to Mysql
#----- MySQL 5.x/6.x
# Comment the embedded database and uncomment the following properties to use MySQL. The validation query is optional.
sonar.jdbc.url: jdbc:mysql://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8
sonar.jdbc.driverClassName: com.mysql.jdbc.Driver
sonar.jdbc.validationQuery: select 1 

Now stop and start the sonar server:
Stop the server : <sonar/installation/directory>/bin/linux-x86-32 : ./sonar.sh stop
Start the server : <sonar/installation/directory>/bin/linux-x86-32 : ./sonar.sh start
By default this server starts on 9000 port.Verify the same on UI : http://localhost:9000
Verify Apporx 43 tables will be created in sonar database in mysql

 
Sample code to show how to use this: Now sonar is up and running perfectly. Let us create sample application that will show the usage of sonar

1. Create a mavenized project say TestSonar in eclipse
2. Create a package with name test.sonar in project
3. Copy the One.java and OneTest.java in test.sonar package
4. Replace pom.xml content with below mentioned pom.xml content
5. From terminal, Go to project path . Run below command:
mvn clean install -Psonar sonar:sonar

One.java:

public class One {
      String message = "foo";
      String message2 = "toto";

      public String foo() {
        return message;
      }

      public String toto() {
        return message2;
      }

      public void uncoveredMethod() {
        System.out.println(foo());
      }
    }

OneTest.java: Create a junit with name OneTest

import static org.junit.Assert.*;

import org.junit.Test;

public class OneTest {

    @Test
      public void testFoo() throws Exception {
        One one = new One();
        assertEquals("foo", one.foo());
      }

      @Test
      public void testBoth() throws Exception {
        One one = new One();
        assertEquals("toto", one.toto());
        assertEquals("foo", one.foo());
      }

}
 
pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/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>org.codehaus.sonar</groupId>
  <artifactId>example-ut-maven-jacoco-runTests</artifactId>
  <version>1.0-SNAPSHOT</version>

  <!-- <name>UT coverage with Maven and JaCoCo running tests</name>-->
  <name>Code coverage with Maven and Sonar running tests</name>


  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <sonar.language>java</sonar.language>

    <!-- Tells Sonar to run the unit tests -->
    <sonar.dynamicAnalysis>true</sonar.dynamicAnalysis>
    <!-- Tells Sonar to use JaCoCo as the code coverage tool -->
    <sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <!-- Minimal supported version is 4.7 -->
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.5</source>
          <target>1.5</target>
        </configuration>
      </plugin>
    </plugins>
  </build>

 
  <!-- BEGIN: Specific to mapping unit tests and covered code -->
  <profiles>
   <profile>
            <id>sonar</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <properties>
                <!-- SERVER ON A REMOTE HOST -->
               <sonar.jdbc.url>jdbc:mysql://localhost:3306/sonar?useUnicode=true&amp;characterEncoding=utf8</sonar.jdbc.url>
                <sonar.jdbc.driverClassName>com.mysql.jdbc.Driver</sonar.jdbc.driverClassName>
                <sonar.jdbc.username>sonar</sonar.jdbc.username>
                <sonar.jdbc.password>sonar</sonar.jdbc.password>
                <sonar.host.url>http://localhost:9000</sonar.host.url>
            </properties>
 </profile>

    <profile>
      <id>coverage-per-test</id>
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <!-- Minimal supported version is 2.4 -->
            <version>2.13</version>
            <configuration>
              <properties>
                <property>
                  <name>listener</name>
                  <value>org.sonar.java.jacoco.JUnitListener</value>
                </property>
              </properties>
            </configuration>
          </plugin>
        </plugins>
      </build>

      <dependencies>
        <dependency>
          <groupId>org.codehaus.sonar-plugins.java</groupId>
          <artifactId>sonar-jacoco-listeners</artifactId>
          <version>1.2</version>
          <scope>test</scope>
        </dependency>
      </dependencies>
    </profile>
  </profiles>
  <!-- END: Specific to mapping unit tests and covered code -->
 
</project>

Once the build will be created successfully, Verify one project with name "Code coverage with Maven and Sonar running tests" will be displayed in sonar UI. Opening the project will show something like below:


OneTest.java has two test cases. You can check the time taken by each test cases
Uncomment or add another testcases to see furthur behaviour


Wednesday, 21 August 2013

Know About Cassandra

Cassandra:

This post will give a brief introduction about one of the NoSQl Database Cassandra

Cassandra in 50 Words or Less
“Apache Cassandra is an open source, distributed, decentralized, elastically scalable, highly available, fault-tolerant, tuneably consistent, column-oriented database that bases its distribution design on Amazon’s Dynamo and its data model on Google’s Bigtable.

Distributed
Cassandra is distributed, which means that it is capable of running on multiple machines while appearing to users as a unified whole.

Decentralized
Cassandra, however, is decentralized, meaning that every node is identical;No node act as master or slave;no Cassandra node performs certain organizing operations distinct from any other node. Instead, Cassandra features a peer-to-peer protocol and uses gossip to maintain and keep in sync a list of nodes that are alive or dead.

The fact that Cassandra is decentralized means that there is no single point of failure. All of the nodes in a Cassandra cluster function exactly the same. This is sometimes referred to as “server symmetry.”

Elastic Scalability ;
Scalability is an architectural feature of a system that can continue serving a greater number of requests with little degradation in performance.

There are 2 types of scaling...
Vertical scaling—simply adding more hardware capacity and memory to your existing machine—is the easiest way to achieve this.

Horizontal scaling means adding more machines that have all or some of the data on them so that no one machine has to bear the entire burden of serving requests.

But then the software itself must have an internal mechanism for keeping its data in sync with the other nodes in the cluster.

Elastic scalability refers to a special property of horizontal scalability. It means that your cluster can seamlessly scale up and scale back down. To do this, the cluster must be able to accept new nodes that can begin participating by getting a copy of some or all of the data and start serving new user requests without major disruption or reconfiguration of the entire cluster. You don’t have to restart your process. You don’t have to change your application queries. You don’t have to manually rebalance the data yourself.
Just add another machine—Cassandra will find it and start sending it work.

Scaling down, of course, means removing some of the processing capacity from your cluster.

High Availability

In general architecture terms, the availability of a system is measured according to its ability to fulfill requests.

Cassandra is highly available. You can replace failed nodes in the cluster with no downtime, and you can replicate data to multiple data centers to offer improved local performance and prevent downtime if one data center experiences a catastrophe such as fire or flood.

The replication factor lets you decide how much you want to pay in performance to gain more consistency. You set the replication factor to the number of nodes in the cluster you want the updates to propagate to (remember that an update means any add, update, or delete operation).

Tuneable Consistency :
Consistency essentially means that a read always returns the most recently written value.
But Cassandra is more accurately termed “tuneably consistent,” which means it allows you to easily decide the level of consistency you require, in balance with the level of availability.

Setup Cassandra Node

Cassandra Installation:

This post will help to setup a cassandra node

1. Download Cassandra from the http://cassandra.apache.org. I am installing apache-cassandra-1.2.3-bin.tar.gz
2. Unzip this file using gunzip apache-cassandra-1.2.3-bin.tar.gz
3. Untar it using tar -xvf apache-cassandra-1.2.3-bin.tar 
4. Modify the cassandra.yaml file. Path of this file will be </path/to/cassandra/installation/conf>
5. In cassandra.yaml you will find the following configuration options:
    initial_token:
    <Generate the token value using ./token-generator tool,Explained in last>
    data_file_directories (/var/lib/cassandra/data),
    commitlog_directory (/var/lib/cassandra/commitlog), and
    saved_caches_directory (/var/lib/cassandra/saved_caches).
    seed_provider:
    # Addresses of hosts that are deemed contact points.
    # Cassandra nodes use this list of hosts to find each other and learn
    # the topology of the ring.  You must change this if you are running
    # multiple nodes!
    - class_name: org.apache.cassandra.locator.SimpleSeedProvider
      parameters:
          # seeds is actually a comma-delimited list of addresses.
          # Ex: "<ip1>,<ip2>,<ip3>"
          - seeds: "192.168.256.78"
   listen_address: localhost
   rpc_address: localhost

Make sure all data directories pre-exist and have writable permission on them

e.g: Updated cassandra.yaml file:
initial_token: 0
saved_caches_directory: /home/apache-cassandra-1.1.0/tmp/var/lib/cassandra/saved_caches
data_file_directories:
    - /home/apache-cassandra-1.1.0/tmp/var/lib/cassandra/data
commitlog_directory: /home/apache-cassandra-1.1.0/tmp/var/lib/cassandra/commitlog
seeds: "192.168.256.78"
Note: seeds takes comma separated list of nodes ip 
listen_address: 192.168.256.78
rpc_address: 192.168.256.78

6. By default, Cassandra will write its logs in /var/log/cassandra/.
Make sure this directory exists and has writable permission,and update log4j-server.properies file:
log4j.appender.R.File=/var/log/cassandra/system.log
Path to log4j-server.properies file will be </path/to/cassandra/installtion/conf> eg:
log4j.appender.R.File=/home/apache-cassandra-.1.0/tmp/var/log/cassandra/system.log


Token Generation: With ./token-generator tool you can generate the tokens for n nodes in cluster and then update the generated value in initial_token property of cassandra.yaml in all nodes respectively.
Path of /token-generator is </path/to/cassandra/installtion/tools/bin>
eg: Generating token for 2 nodes:
./token-generator 2
  DC #1:
  Node #1:                                        0
  Node #2:   85070591730234615865843651857942052864

Now update initial_token with generated values in cluster

Start Cassandra:
Start the cassandra daemon using 'bin/cassandra -f' 
The service should start in the foreground and log gratuitously to the console
If you do not want to see log on screen use it without -f option
Without using "-f " option, it will run in the background.
It will start a CassandraDaemon which can be checked using jps

Check the cluster state:
bin/nodetool -h <node_ip> ring

Stop Cassandra:
You can stop the process by killing it, using 'pkill -f CassandraDaemon'

Wednesday, 19 December 2012

Hadoop MapReduce Chaining


Small data processing tasks can be accomplished by a single MapReduce job but complex tasks need to be broken down into simpler subtasks, and each should be accomplished by an individual MapReduce job 

Chaining MapReduce jobs in a sequence :
Two jobs can be executed manually one after the other, it’s more convenient to automate the execution sequence. You can chain MR jobs to run sequentially, with the output of one MapReduce job being the input to the next. 
Chaining MapReduce jobs is analogous to Unix pipes.

mapreduce-1 | mapreduce-2 | mapreduce-3 | ...

Chaining MapReduce jobs involves calling the driver of one MapReduce job after another. The driver at each job will have to create a new JobConf object and set its input path to be the output path of the previous job. You can delete the intermediate data generated at each step of the chain at the end.

Chaining MapReduce jobs with complex dependency :

Sometimes the sub tasks of a complex data processing task don’t run sequentially, and their MapReduce jobs are therefore not chained in a linear fashion. For example,mapreduce1 may process one data set while mapreduce2 independently processes another data set. The third job, mapreduce3, performs an inner join of the first two jobs output.It’s dependent on the other two and can execute only after both mapreduce1 and mapreduce2 are completed. But mapreduce1 and mapreduce2 aren’t dependent on each other.

Hadoop has a mechanism to simplify the management of such (nonlinear) job dependencies via the Job and JobControl classes. A Job object is a representationof a MapReduce job. You instantiate a Job object by passing a JobConf object to its constructor. In addition to holding job configuration information, Job also holds dependency information, specified through the addDependingJob() method. For
Job objects x and y,  
x.addDependingJob(y) 
means x will not start until y has finished. Whereas Job objects store the configuration and dependency information, JobControl objects do the managing and monitoring of the job execution. You can add jobs to a JobControl object via the addJob() method.

After adding all the jobs and dependencies, call JobControl’s run() method to spawn a thread to submit and monitor jobs for execution. JobControl has methods like allFinished() and getFailedJobs() to track the execution of various jobs within the batch.

You can think of chaining MapReduce jobs, using the pseudo-regular expression: 

[MAP | REDUCE]+
where a reducer REDUCE comes after a mapper MAP, and this [MAP | REDUCE] sequence can repeat itself one or more times, one right after another. 

The analogous expression for a job using ChainMapper and ChainReducer would be
MAP+ | REDUCE | MAP*
The job runs multiple mappers in sequence to preprocess the data, and after running reduce it can optionally run multiple mappers in sequence to postprocess the data.The beauty of this mechanism is that you write the pre- and postprocessing steps as standard mappers. You can run each one of them individually if you want. 


Let’s look at the signature of the ChainMapper.addMapper() method to understand in detail how to add each step to the chained job. The signature and function of ChainReducer.setReducer() and ChainReducer.addMapper() are analogous 

public static <K1,V1,K2,V2> void addMapper(JobConf job,
             Class<? extends Mapper<K1,V1,K2,V2>> klass,
             Class<? extends K1> inputKeyClass,
             Class<? extends V1> inputValueClass,
             Class<? extends K2> outputKeyClass,
             Class<? extends V2> outputValueClass,
             boolean byValue,
             JobConf mapperConf)

This method has eight arguments. The first and last are the global and local JobConf objects, respectively. The second argument (klass) is the Mapper class that will do the data processing. The four arguments inputValueClass, inputKeyClass, outputKeyClass, and outputValueClass are the input/output class types of the Mapper class.
 
Please see the next blog which demonstrates the usage of Chaining in MapReduce jobs

For any query please drop a comment...... 

Hadoop MapReduce Chaining Example

As discussed in previous post [Hadoop MapReduce Chaining ]Here i will apply the mapper/reducer chaining to wordcount example

I will follow the following sequence of chaining in my job:
MAP+ | REDUCE | MAP* 
The job runs multiple mappers in sequence to preprocess the data, and after running reducer, it will run multiple mappers in sequence to postprocess the data.Mappers before reduce phase can be called preprocessing of data and Mappers after reduce phase can be called postprocessing of data

This job Consists of following classes:
  • ChainWordCountDriver 
  • TokenizerMapper
  • UpperCaserMapper
  • WordCountReducer
  • LastMapper
ChainWordCountDriver will take input file which should be seperated on token. It will call different mappers and reducers in following sequence
TokenizerMapper -- > UpperCaserMapper -- > WordCountReducer -->  LastMapper
Here the output of one phase will become the input of next phase

public class ChainWordCountDriver extends Configured implements Tool {

    public int run(String[] args) throws Exception {
        JobConf conf = new JobConf(getConf(), ChainWordCountDriver.class);
        conf.setJobName("wordcount");
     
        Path outputPath = new Path("/home/impadmin/testdata/CustomerOutput");
        FileSystem  fs = FileSystem.get(new URI(outputPath.toString()), conf);
        //It will delete the output directory if it already exists. don't need to delete it  manually  
        fs.delete(outputPath);
      
        //Setting the input and output path
        FileInputFormat.setInputPaths(conf, "/home/impadmin/testdata/Customer");
        FileOutputFormat.setOutputPath(conf, outputPath);

        //Considering the input and output as text file set the input & output format to TextInputFormat
        conf.setInputFormat(TextInputFormat.class);
        conf.setOutputFormat(TextOutputFormat.class);

        JobConf mapAConf = new JobConf(false);
        ChainMapper.addMapper(conf, TokenizerMapper.class, LongWritable.class, Text.class, Text.class, IntWritable.class, true, mapAConf);     
        
            //addMapper will take global conf object and mapper class ,input and output type for this mapper and output key/value have to be sent by value or by reference and localJObconf specific to this call
       
        JobConf mapBConf = new JobConf(false);
        ChainMapper.addMapper(conf, UpperCaserMapper.class, Text.class, IntWritable.class, Text.class, IntWritable.class, true, mapBConf);

        JobConf reduceConf = new JobConf(false);
        ChainReducer.setReducer(conf, WordCountReducer.class, Text.class, IntWritable.class, Text.class, IntWritable.class, true, reduceConf);

       JobConf mapCConf = new JobConf(false);
       ChainReducer.addMapper(conf, LastMapper.class, Text.class, IntWritable.class, Text.class, IntWritable.class, true, mapCConf);

        JobClient.runJob(conf);
        return 0;
    }

    public static void main(String[] args) throws Exception {
        int res = ToolRunner.run(new Configuration(), new ChainWordCountDriver(), args);
        System.exit(res);
    }
} 

TokenizerMapper  -  Parse the input file record for every token
public class TokenizerMapper extends MapReduceBase implements Mapper<LongWritable, Text,Text, IntWritable> {
    private final static IntWritable one = new IntWritable(1);
    private Text word = new Text();

    public void map(LongWritable key, Text value,OutputCollector output,Reporter reporter) throws IOException {
        String line = value.toString();
        System.out.println("Line:"+line);
        StringTokenizer itr = new StringTokenizer(line);
        while (itr.hasMoreTokens()) {
            word.set(itr.nextToken());
            output.collect(word, one);
        }
    }
}

UpperCaserMapper - It will uppercase the passed token from TokenizerMapper
 public class UpperCaserMapper extends MapReduceBase implements Mapper<Text, IntWritable,Text, IntWritable> {

    public void map(Text key, IntWritable value,OutputCollector output,Reporter reporter) throws IOException {
        String word = key.toString().toUpperCase();
        System.out.println("Upper Case:"+word);
        output.collect(new Text(word), value);   
    }
}

WordCountReducer - is doing nothing special just writing the key in the context
 public  class WordCountReducer extends MapReduceBase implements Reducer<Text, IntWritable,Text, IntWritable> {

    public void reduce(Text key, Iterator values,OutputCollector output, Reporter reporter) throws IOException {
        int sum = 0;
        output.collect(key, new IntWritable(sum));
    }
}

LastMapper - will spilt the record sent from reducer and write into the final output file
 public class LastMapper extends MapReduceBase implements Mapper<Text, IntWritable,Text, IntWritable> {
   
    public void map(Text key, IntWritable value,OutputCollector output,Reporter reporter) throws IOException {
        String[] word = key.toString().split(",");
        System.out.println("Upper Case:"+word);
        output.collect(new Text(word[0]), new Text(word[1]));   
    }
}

Input data is like this:
customerId,customerName,contactNumber
1,Stephanie Leung,555-555-5555
2,Edward Kim,123-456-7890
3,Jose Madriz,281-330-8004
4,David Stork,408-555-0000

Here the order of execution will be :
1.Driver will call the mappers and reducers in the following sequence.
2. Record will be read in TokenizerMapper, it will parse and split the record on each token[space] and sent it to UpperCaserMapper
3. UpperCaserMapper will do the uppercase of record and send it to  WordCountReducer
4. WordCountReducer will just write the key
5. LastMapper will again split the key written by reducer on comma and write this


For any query please drop a comment......

Tuesday, 4 September 2012

HDFS Client API

In this post, let us explore Hadoop file system basic APIs

  • Create a directory in hdfs
  • Copy a file from local files system to hdfs
  • Read the hdfs file
  • Delete the hdfs file
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;

public class HDFSClient {
    public static Configuration conf = new Configuration();
   
    static{
        conf.addResource(new Path("/home/impadmin/sws/hadoop-0.20.2/conf/core-site.xml"));
    }
   
    public static void createHdfsfile(String fromLocalFile,String hdfsFile) throws IOException {
        FileSystem hdfs = FileSystem.get(conf);
        Path localDir = new Path(fromLocalFile);
        Path hdfsDir = new Path(hdfsFile);
        hdfs.copyFromLocalFile(localDir, hdfsDir);
    }

    public static void readFile(String file) {
        FileSystem fileSystem;
        try {
           fileSystem = FileSystem.get(conf);
            Path path = new Path(file);
        if (!fileSystem.exists(path)) {
            System.out.println("File " + file + " does not exists");
            return;
        }
        FSDataInputStream in = fileSystem.open(path);
        String filename = file.substring(file.lastIndexOf('/') + 1,
                file.length());
        OutputStream out = new BufferedOutputStream(new FileOutputStream(
                new File(filename)));
        byte[] b = new byte[1024];
        int numBytes = 0;
        while ((numBytes = in.read(b)) > 0) {
            out.write(b, 0, numBytes);
        }
        String s = new String(b);
        System.out.println(s);
        in.close();
        out.close();
        fileSystem.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void deleteFile(String file) throws IOException {
        FileSystem fileSystem = FileSystem.get(conf);
        Path path = new Path(file);
        if (!fileSystem.exists(path)) {
            System.out.println("File " + file + " does not exists");
            return;
        }
        boolean success = fileSystem.delete(new Path(file), true);
        fileSystem.close();
    }

    public static void mkdir(String dir) throws IOException {
        FileSystem fileSystem = FileSystem.get(conf);
        Path path = new Path(dir);
        if (fileSystem.exists(path)) {
            System.out.println("Dir " + dir + " already exists...");
            return;
        }
        boolean success = fileSystem.mkdirs(path);
        fileSystem.close();
    }

    public static void main(String[] args) throws IOException {
        //Create a Directory in HDFS
        HDFSClient.mkdir("hdfs://localhost:9000/user/impadmin/testDir");
       
        String srcFile = "file:///home/impadmin/testdata/Order";
        String destFile = "hdfs://localhost:9000/user/impadmin/testDir";
      
       //Copy File from local file system to hdfs
        HDFSClient.createHdfsfile(srcFile,destFile);

        //Read the file
        HDFSClient.readFile("hdfs://localhost:9000/user/impadmin/testDir/Order");
       
        //Delete The File
        HDFSClient.deleteFile("hdfs://localhost:9000/user/impadmin/testDir/Order");
        }
}

For any query please drop a comment......

Monday, 27 August 2012

Hbase Map Reduce : Demo MR job - Import tsv file to hbase table

Map Reduce job to demonstrate how to import an tsv file from hdfs to hbase table:

Let us start by creating a hbase table:

We have a table 'sample3 '

create 'sample3','region','time','product','sale','profit'
Here 'region','time','product','sale','profit' are different colfamily of the table

Structue of the table is :
hbase(main):043:0> describe 'sample3'
DESCRIPTION                                                                                                                                              ENABLED                                                                           
 {NAME => 'sample3', FAMILIES => [{NAME => 'product', BLOOMFILTER => 'NONE', REPLICATION_SCOPE => '0', COMPRESSION => 'NONE', VERSIONS => '3', TTL => '2 true                                                                              
 147483647', BLOCKSIZE => '65536', IN_MEMORY => 'false', BLOCKCACHE => 'true'}, {NAME => 'profit', BLOOMFILTER => 'NONE', REPLICATION_SCOPE => '0', COMP                                                                                   
 RESSION => 'NONE', VERSIONS => '3', TTL => '2147483647', BLOCKSIZE => '65536', IN_MEMORY => 'false', BLOCKCACHE => 'true'}, {NAME => 'region', BLOOMFIL                                                                                   
 TER => 'NONE', REPLICATION_SCOPE => '0', COMPRESSION => 'NONE', VERSIONS => '3', TTL => '2147483647', BLOCKSIZE => '65536', IN_MEMORY => 'false', BLOCK                                                                                   
 CACHE => 'true'}, {NAME => 'sale', BLOOMFILTER => 'NONE', REPLICATION_SCOPE => '0', COMPRESSION => 'NONE', VERSIONS => '3', TTL => '2147483647', BLOCKS                                                                                   
 IZE => '65536', IN_MEMORY => 'false', BLOCKCACHE => 'true'}, {NAME => 'time', BLOOMFILTER => 'NONE', REPLICATION_SCOPE => '0', COMPRESSION => 'NONE', V                                                                                   
 ERSIONS => '3', TTL => '2147483647', BLOCKSIZE => '65536', IN_MEMORY => 'false', BLOCKCACHE => 'true'}]}                       e


Let us have a sample tsv(Tab seperated file) having sample data like shown below:
1    India    Haryana    Chandigarh    2009    April    P1    1    5
2    India    Haryana    Ambala    2009    May    P1    2    10
3    India    Haryana    Panipat    2010    June    P2    3    15
4    United States    California    Fresno    2009    April    P2    2    5
5    United States    California    Long Beach    2010    July    P2    4    10
6    United States    California    San Fransico    2011    August    P1    6    20

Note: Place this file in HDFS

Let us have a look at the map reduce job code:

import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.TableOutputFormat;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;

public class ImportFromFile {
   
   
    static class ImportMapper extends
            Mapper<LongWritable, Text, ImmutableBytesWritable, Writable> {
   
        @Override
        public void map(LongWritable offset, Text line, Context context)
                throws IOException {
            try {
                String lineString = line.toString();
                String[] arr = lineString.split("\t");
                //Apply boundary checks according to your tsv file
                Put put = new Put(arr[0].getBytes());
                put.add("region".getBytes(), "country".getBytes(), Bytes.toBytes(arr[1]));
                put.add("region".getBytes(),"state".getBytes(), Bytes.toBytes(arr[2]));
                put.add("region".getBytes(),"city".getBytes(), Bytes.toBytes(arr[3]));
                put.add("time".getBytes(),"year".getBytes(), Bytes.toBytes(arr[4]));
                put.add("time".getBytes(),"month".getBytes(), Bytes.toBytes(arr[5]));
                put.add("product".getBytes(),"productid".getBytes(), Bytes.toBytes(arr[6]));
                put.add("sale".getBytes(),"unit".getBytes(), Bytes.toBytes(arr[7]));
                put.add("profit".getBytes(),"total".getBytes(), Bytes.toBytes(arr[8]));
                       context.write(new ImmutableBytesWritable(arr[0].getBytes()), put);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    public static void main(String[] args) throws Exception {
        Configuration conf = HBaseConfiguration.create();
        String table = "sample3";
        String input = "/home/impadmin/testdata/hbaseolap/olapdata";
        String column = "";

        conf.set("conf.column", column);
        Job job = new Job(conf, "Import from file " + input + " into table "
                + table);
        job.setJarByClass(ImportFromFile.class);
        job.setMapperClass(ImportMapper.class);
        job.setOutputFormatClass(TableOutputFormat.class);
        job.getConfiguration().set(TableOutputFormat.OUTPUT_TABLE, table);
        job.setOutputKeyClass(ImmutableBytesWritable.class);
        job.setOutputValueClass(Writable.class);
        job.setNumReduceTasks(0);
        FileInputFormat.addInputPath(job, new Path(input));
        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }

}
After running this, file is imported in hbase table.

Cheers
Geetanjali

For any query please drop a comment......