Tuesday, January 11, 2011

Distributed Processing Made Easy with GridGain 3.0

Parallel computing is a popular topic, seen by the growing number of cores in CPU's, as well as, software frameworks that take advantage of them. While some types of computer hardware have managed to keep up with rapid advancements in chip design (memory and graphics), some devices have failed to match the pace. We are still largely constrained by I/O limitations (both in terms of storage and network throughput). While it is possible to "scale up" the hardware of a single machine to meet the increasing demands of an application, the more practical solution is to distribute tasks amongst a cluster of machines.

The Java Community is blessed with a plethora of distributed processing frameworks. Perhaps the most ubiquitous in this category are Apache Hadoop and Terracotta. If you are not familiar with the two, Terracotta allows engineers to cluster core components of a JVM across multiple machines. Hadoop, on the other hand, is an ecosystem of tools centered around the distributed Map-Reduce algorithm; this is an oversimplification of both frameworks, but it should work for the context of this discussion.

The strategies employed by both Terracotta and Hadoop represent the two schools of thought in distributed frameworks. Terracotta's opinion is that the effort of distributing an application should be the burden of the framework, and the concern of parallelism separated from the application. In this case, turning a standard Java application into a grid application is transparent to the developer. Distributed processing in Apache Hadoop is a lot more deliberate. Hadoop is not designed to be deployed in some web container, coexisting with CRUD applications. Hadoop also is not designed to scale a POJO application. Hadoop is designed specifically to accomplish one goal: storing and processing massive amounts of data.

Unfortunately, there are many cases where we have a need existing somewhere in between the intended goals of both frameworks. In many cases, we may have some CRUD application that needs to distribute processing or offload computationally expensive tasks, but does not need a distributed JVM or cluster of specially purposed machines. This is not to say that Terracotta or Hadoop could not be made to serve this role with some effort.

An excellent framework that (sort of) exists between the goals of Terracotta and Hadoop is GridGain (http://www.gridgain.com).  GridGain allows engineers to easily adapt existing applications into ones capable of distributing their loads across a cluster of machines.  GridGain's strategy for achieving this functionality is a beautiful blend of annotations and advice (see Aspected Oriented Programming) applied to your existing application code.  More importantly, GridGain is easy and enjoyable.  Using the framework feels very natural because the gentlemen at GridGain have gone to painstaking efforts to include the core features most of us use in standard Java development: Spring, JUnit, application container accessible, etc.  I should also mention that the API is also well designed; my litmus test for design is stability (just works) and intuitiveness (understand the abstraction without needing to consult documentation).  All of the example code in this post was written in only a couple of hours!

Here are some of the other features of GridGain:
  • Ad Hoc, Zero-Config Grids!  Grid Nodes broadcast their existence on the network and auto discover other nodes.  This allows you to easily scale your grid up or down just by starting or stopping nodes.
  • Map-Reduce capability.
  • Queryable In-Memory Cache.
  • Grid Event Model.  React to a number of events on the grid (cache insertions/deletions, nodes discovered, etc).
  • Distributed Thread-Pool.
  • Functional Language support for Java.
  • Native Scala support.
You can read more about GridGains features from their White Paper: http://www.gridgain.com/media/gridgain_white_paper.pdf.

The power of GridGain is best seen in a code demonstration.  So instead of continuing to espouse the merits of the framework, why don't we just jump into some code?  The remainder of the post will center around 3 separate Grid Applications.  The post is rather long, so I've left a little roadmap to each individual application below:
  1. Using the Gridify attribute to distribute a task to the Grid.
  2. Broadcasting the execution of a classic thread task (Runnable) across all nodes in the Grid.
  3. Turning a classic Java function into a distributed Map-Reduce operation.
Sorry for the long-winded introduction.  Without further adieu, here is our first demonstration: Using the Gridify attribute to distribute a task to the Grid.

Demo 1.

The following is an example distributing a method to a grid node in the Ad Hoc cluster. The method annotated with "Gridify" will be advised by an AOP framework with a proxy that distributes the work amongst the active grid nodes. Since this task is not associated with a TaskSplitter implementation, it can only be ran on a single node (chosen at random on the network).

package com.berico.grid;

import org.gridgain.grid.GridException;
import org.gridgain.grid.gridify.Gridify;
import org.gridgain.grid.gridify.aop.spring.GridifySpringEnhancer;
import org.gridgain.grid.typedef.G;

 * Demonstrates the use of GridGain's 
 * Gridify annotation for distributed
 * processing of a method.
 * @author Richard C. (Berico Technologies)
public class GridifyExample {

   * Entry-point to the application
   * @param args Command Line arguments
   * @throws GridException 
  public static void main(String[] args) 
   throws GridException {
    //Programatically start a new
    //Grid node instance
    //Wrap the call so we can close
    //the connection regardless of
    //what happens.
    try {
      //Create a new instance of this
      //class.  Technically this is not
      //necessary; the annotation can be
      //applied to a static method.
      //We, however, need to use Spring AOP
      //to advise this class (proxying the
      //object with the Distributed functionality
      //at runtime, vice compile-time).
      GridifyExample example = new GridifyExample();
      //This step is unnecessary if AspectJ
      //or JBoss AOP is correctly configured
      //to proxy objects annotated with
      example = GridifySpringEnhancer.enhance(example);
      //Call the distribute method.  Behind
      //the scenes, this method is proxied and
      //will be invoked using GridGain's
      //distributed api.
    finally {
      //Stop the local grid node (whether
      //we succeed or fail).

   * A node on the grid will be tasked with 
   * executing this method.  Since the 
   * "Gridify" annotation is not paired
   * with a task splitter, this will only
   * run on one grid node (randomly chosen).
  public void distribute(){
      "Calling Gridified Method.");

The following image shows only one Grid Node outputting the message (keep in mind, Eclipse is serving as one of those nodes):

Full View:  http://dl.dropbox.com/u/12311372/GridifyExample.Output.PNG

Demo 2.

The previous example was not particularly useful. Sure, I can think of a couple of scenarios that might benefit by randomly distributing a single task to another node, but these scenarios are far less common than the need to perform map-reduce or queue a static job on multiple servers.

The next example demonstrates how to force all nodes in the Grid to execute a standard Java "Runnable" implementation. For this demonstration, our Runnable task will simply print a message to the console (don't worry, I'll provide a demonstration of working with state in the next example). Here is the Runnable implementation:

 * An implementation of the standard 
 * Java Runnable Interface
 public static class MyGridTask implements Runnable {
   //This will be executed by each Grid node.
   public void run() {
     System.out.println("Got a Task!");

To run this task across all servers in our grid, all we need to do is submit the Runnable to the grid's run method:

package com.berico.grid;

import org.gridgain.grid.GridClosureCallMode;
import org.gridgain.grid.GridException;
import org.gridgain.grid.typedef.G;

 * An Example of using a Zero-Config Grid
 * to broadcast the execution of a Java
 * Runnable.
 * @author Richard C (Berico Technologies)
 public class RunnableExample {

   * Start a new Grid Node and 
   * broadcast a "Runnable" job on
   * all participating nodes. 
   * @param args Shell Arguments
   * @throws GridException
   public static void main(String[] args) 
         throws GridException{

     //Start a new Grid Node instance
     try {
       //Get a handle to the Grid
           //Broadcast a run request
                new MyGridTask());
     } finally {
       //Stop the Grid Node instance

This is the output on the initiating node:

[23:34:52]    _____     _     _______      _         ____  ___    
[23:34:52]   / ___/____(_)___/ / ___/___ _(_)___    |_  / / _ \  
[23:34:52]  / (_ // __/ // _  / (_ // _ `/ // _ \  _/_ <_/ // /  
[23:34:52]  \___//_/ /_/ \_,_/\___/ \_,_/_//_//_/ /____(+)___/
[23:34:52]        ---==++ C0MPUTE + DATA + CL0UD ++==---
[23:34:52]                 ver. 3.0.3c-20122010
[23:34:52]    2005-2010 Copyright (C) GridGain Systems, Inc.
[23:34:52] Quiet mode.
[23:34:52]   ^-- To disable add -DGRIDGAIN_QUIET=false or "-v" to ggstart.{sh|bat}
[23:34:52] << Community Edition >>
[23:34:52] Language runtime: Java Platform API Specification ver. 1.6
[23:34:52] GRIDGAIN_HOME=null
[23:34:54] Node JOINED [nodeId8=165ec9f5, addr=[], CPUs=2]
[23:34:54] '--ToPoLoGy SnApShOt [nodes=2, CPUs=2, hash=0xFCCE1F62]
[23:34:54] Node JOINED [nodeId8=ec1ddad5, addr=[], CPUs=2]
[23:34:54] '--ToPoLoGy SnApShOt [nodes=3, CPUs=2, hash=0xB32DDF10]
[23:34:57] JVM: Sun Microsystems Inc., Java(TM) SE Runtime Environment ver. 1.6.0_23-b05
[23:34:57] OS: Windows 7 6.1 amd64, rclayton
[23:34:57] VM name: 20960@deathstar
[23:34:57] Local ports: TCP:47102 UDP:47200 TCP:47302 
[23:34:57] GridGain started OK [grid=default, nodeId8=1ad101c5, CPUs=2, addrs=[]]
[23:34:57] ZZZzz zz z...
Got a Task!
[23:34:57] GridGain stopped OK [uptime=00:00:00:258]

And the output from all of the Grid Nodes:

Full View: http://dl.dropbox.com/u/12311372/RunnableExample.Output.PNG

Demo 3.

Of course, distributing work that doesn't return any data to nodes throughout the network isn't very exciting. More often than not, we are going to want to pass some data to the grid nodes and get a synthesized dataset back. GridGain has a number of mechanisms for doing this sort of task. Let's see an example very similar to a map-reduce job that you might submit to Hadoop.

In this scenario, we have a large set of keys (UUID's) and we need to find the correct server that holds the data that key is associated with. This is a common problem we encounter when building distributed databases. My approach to "consistent key partitioning" is to use the remainder of the value of the 3rd and 4th byte of a UUID (lowest order bytes) divided by the number of servers in the cluster.

server index = ((integer formed from concat(byte[2], byte[3])) % number of servers)

This strategy works surprisingly well at evenly distributing UUID keys amongst servers. This is because many UUID implementations (a lot of Databases) increment the first 4 bytes of the UUID (keeping the trailing 12 bytes consistent) because it is more performant for indexing (though this strategy may not necessarily be standards compliant). To ensure we understand the exact algorithm being performed by the distributed nodes, review the implementation below; we're going to use a really simple class to store both the string representation of the UUID and, eventually, the server index that key is stored on. For the sake of brevity, and the fact that the class is used internally, I'm not going to use accessors and mutators on the data structure.

package com.berico.grid;

import java.io.Serializable;

 * Associates a record's key to a server.
 * This is necessary when records are 
 * partitioned across a cluster of servers.
 * Note, I chose not to use getters or
 * setters.  This class used internally.
 * @author Richard C. (Berico Technologies)
 public class KeyToServerAssociation implements Serializable {

   private static final long 
      serialVersionUID = 4299712504127354446L;

   //The Key of a record; UUID without
   //the dashes.
   public String key;
   //The index/id of the server
   //the key should be stored or
   public int server;
    * Friendly toString
   public String toString(){
     StringBuilder sb = new StringBuilder();
     sb.append("[Key: ")
       .append(" Server: ")
     return sb.toString();

And here is the static method we will use to calculate the association. This method exists in the LobUuidAssociationBuilder class (I will introduce it a little later).

 * We want the 3rd and 4th bytes of the UUID, 
 * which is the least significant
 * 2 bytes of the 32-bit (4 byte) integer. 
 * Most databases increment this value
 * when they create their UUIDs.
 * The X's represent the part of 
 * the string we are targeting
 * on the GUID:  
 * 000000XX--0000-0000-0000-000000000000
 * @param association Key to Server Association
 public static void associate(
         KeyToServerAssociation association){
   //Get the least significant 2 bytes by
   //parsing the 7th and 8th characters
   //of the UUID string
   Integer leastSignificantBytes = 
        association.key.substring(6, 8), 16);
   //We will take the remainder of the 
   //least significant bytes divided by
   //the number of servers as the index
   //of the appropriate server in which
   //the record is stored.
   association.server = 
   leastSignificantBytes % NUMBER_OF_SERVERS;
   //For the sake of the demonstration,
   //print the server
       "Key associated with server: %s", 

The goal of this application is to build a list of associations for a million or more keys. So we need an implementation in which we can submit a list of unassociated KeyToServerAssociation objects and receive an equivalent list back with the server indexes set. Our interface for this implementation is listed below:

package com.berico.grid;

import java.util.ArrayList;

import org.gridgain.grid.gridify.Gridify;

 * Given a list of unassociated keys (meaning
 * only the UUID has been set, not the
 * server index), associate those keys
 * to the appropriate servers and return
 * the list.
 * @author Richard C. (Berico Technologies)
 public interface IBulkAssociationBuilder {
    * Build the associations and return
    * it to the caller
    * @return Key to Server Association
    @Gridify(taskClass = KeyAssociationTaskSplitter.class)
    ArrayList<KeyToServerAssociation> build(
      ArrayList<KeyToServerAssociation> unassociatedKeys);


Probably the first thing you will notice is the addition of some metadata to the "Gridify" annotation. When we start dealing with functions that can be distributed across multiple servers, we need to have a mechanism to decide how the task will be split amongst the server nodes. It is also essential to determine how the results from all those nodes will be "reduced" back into a single result. This is the job of the GridGain TaskSplitter class. In our example, we extend GridGain's API creating a custom TaskSplitter for our Grid Application (this is the standard way we do this form of distributed processing using the framework).

package com.berico.grid;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.gridgain.grid.GridException;
import org.gridgain.grid.GridJob;
import org.gridgain.grid.GridJobAdapterEx;
import org.gridgain.grid.GridJobResult;
import org.gridgain.grid.gridify.GridifyArgument;
import org.gridgain.grid.gridify.GridifyTaskSplitAdapter;

 * This is GridGains mechanism for splitting
 * tasks (you get to decide how jobs are 
 * partitioned), and optionally allows you
 * to reduce the results into some type of
 * value (in our case, a list of key-to-server
 * associations).
 * @author Richard C. (Berico Technologies)
 public class KeyAssociationTaskSplitter 
        extends GridifyTaskSplitAdapter
               < List<KeyToServerAssociation> > {

   //For the Serializable interface
   private static final long 
      serialVersionUID = -6072502274009699541L;

    * Split the work traditionally done by the
    * build() method into a collection of grid
    * jobs that can be distributed to other
    * nodes.
    * @param gridSize Number of nodes in the grid
    * @param argument The Method
    * @return
    * @throws GridException
   protected Collection<? extends GridJob> 
         split(int gridSize, GridifyArgument argument)
              throws GridException {
     //Pull the KeyAssociator from the Gridify AOP Argument
     LobUuidAssociationBuilder associationBuilder = 
     //Total number of unassociated keys in the List
     int totalRecords = 
     //A Collection of Grid Jobs we're going to 
     //submit to the GridGain framework to distribute
     Collection<GridJobAdapterEx> jobs 
         = new ArrayList<GridJobAdapterEx>();
     //Get a local reference to the list of unassociated keys
     List<KeyToServerAssociation> associations 
         = associationBuilder.getUnassociatedKeys();
     //We are going to partition the unassociate keys list
     //evenly across the Grid Nodes.  To do this, we will
     //need to calculate the partition size by dividing
     //the total number of records by the number of 
     //server nodes in the grid
     int partitionSize = Math.round(totalRecords / gridSize);
     //We need to keep track of what server we are on
     //when we partition the list.  This variable will hold
     //that 'position'.
     int position = 0;
     //While we haven't reached the last node in the grid
     while(position < (gridSize)){
       //Calculate the starting position for this
       //partition on the list
       int startingIndex = position * partitionSize;
       //Reference to the end position for the partition
       //on the list
       int endingIndex;
       //If this is not the last partition
       if(position != (gridSize -1)){
         //the end position is the current partition we
         //are on multiplied by the partition size
         endingIndex = (position + 1) * partitionSize;
       //If this is the last position
       else {
         //the ending index is the last item in 
         //the array list
         endingIndex = totalRecords - 1;
       //Create a new list to hold the "splice" (sublist)
       //of the initial unassociated key list
       ArrayList<KeyToServerAssociation> splice 
           = new ArrayList<KeyToServerAssociation>();
       //Now that we have the index we should start and end
       //at, iterate through the array adding those items
       //to the spliced list.
       for(int r = startingIndex; r < endingIndex + 1; r++){
       //Once we have the spliced list, we need to add a new
       //Grid Job to the the Grid Job list.  This is an atomic
       //unit of work for a grid node in GridGain.
         //Create a new Job Adapter, essentially a wrapper
         //for the method we want to distribute.
         new GridJobAdapterEx(splice) {
           //Since this is sent over the wire to another
           //server node, it is Serializable
           private static final long 
             serialVersionUID = 1243523452452345L;
           //The method that will be executed by
           //the remote grid node.  Please note that
           //the implementation requires a Serializable
           //object (since it is sent back over the wire).
           //This is why you see me using ArrayList instead
           //of the List Interface (which does not extend
           @Override public Serializable execute() {
             //Create a new instance of the same class
             //that we are initially wrapping; this
             //could alternatively be another class,
             //but it's convenient to only have one
             //implementation and treat an instance
             //methods as if it were static to prevent
             //duplication of the functionality.
             LobUuidAssociationBuilder taskWrapper 
                 = new LobUuidAssociationBuilder();
             //Finally, build the associations.
             //It took me a minute to realize we are
             //calling an inherited template method 
             //which returns the constructor argument 
             //(the key-to-server association list)
             //as its proper type.
             return taskWrapper.build(
         //Now that the job is added, increment the partition
         //position (to the next node).
       //Return the grid job list.
       return jobs;

   * Reduce the results from all "mapped" job
   * nodes into a single KeyToServerAssociation list
   * result.
   * @param results The result list from all
   * grid nodes
   * @return Returns all of the completed key to 
   * server associations 
   public List<KeyToServerAssociation> 
                reduce(List<GridJobResult> results)
                     throws GridException {
     //Create a new ArrayList to hold the 
     //merged results
     ArrayList<KeyToServerAssociation> associations 
          = new ArrayList<KeyToServerAssociation>();
     //Iterate over the results
     for (GridJobResult result : results) {
       //Add the resultant data from the grid
       //nodes to the merge array
     //Return the merged list
     return associations;

In order to Gridify our IBulkAssociationBuilder.build() method, we need to implement the class. This is our "Lowest Order Bytes" implementation that we talked about above. I've abbreviated the associate() method since we've already seen it (see the code above). The one thing to mention about this class is that it contains methods to store the state that's going to be "Gridify"'d. This is probably the one thing I don't like very much, since we have to store all of the state (all key-to-server associations) in the advised class (so it can't be split by the task splitter). Of course, I can think of other ways to perform the task, but not too many seem very clean.

package com.berico.grid;

import java.util.ArrayList;

 * Lowest Order Bytes, Association Builder.
 * Associates the key (a UUID) to the appropriate
 * server.  The correct server is calculated
 * using a modulus operation on the UUID's
 * 3rd and 4th byte by the number of servers
 * in the cluster.
 * @author Richard C. (Berico Technologies)
 public class LobUuidAssociationBuilder 
       implements IBulkAssociationBuilder {
   //Number of Servers in the cluster in
   //which we are partitioning keys
   public static int NUMBER_OF_SERVERS = 16;
   //Stored Key to Server Associations
   private ArrayList<KeyToServerAssociation> associations;
   public LobUuidAssociationBuilder(){}
    * Initialize the task, passing the 
    * unassociated keys
    * @param associations Unassociated Keys
   public LobUuidAssociationBuilder(
           ArrayList<KeyToServerAssociation> associations){
     this.associations = associations;
    * Get the unassociated keys
    * @return Unassociated Keys
   public ArrayList<KeyToServerAssociation> 
             getUnassociatedKeys() {
     return this.associations;
    * Build the Associations
    * @return Key to Server Associations
   public ArrayList<KeyToServerAssociation> build(
            ArrayList<KeyToServerAssociation> unassociatedKeys){
     //Iterate over the collection
     for(KeyToServerAssociation association : unassociatedKeys){
     //Return the Association
     return unassociatedKeys;
   * @param association Key to Server Association
   public static void associate(
     //And we've seen this method before

To be completely transparent, the next class demonstrates how I'm generating and temporarily storing the keys. It's not essential for the purposes of explaining the framework, but I've included just in case you had questions:

package com.berico.grid;

import java.util.ArrayList;
import java.util.UUID;

 * Generates unassociated keys for our grid
 * demonstration.
 * @author Richard C. (Berico Technologies)
 public class TestDataGenerator {
   //Number of keys to generate
   public static int NUMBER_OF_KEYS = 1000000;
   //A list of generated unassociated keys
   protected ArrayList<KeyToServerAssociation> associations;
    * Default Constructor
   public TestDataGenerator(){}
   * Setup the example by creating 
   * a bunch of random keys.
   public void generate(){
     //initialize the list
         = new ArrayList<KeyToServerAssociation>();
     //generate a bunch of unassociated keys
     for(int i = 0; i < NUMBER_OF_KEYS; i++){
   * Get the list of key to server associations
   * @return
   public ArrayList<KeyToServerAssociation> 
     //Return the list of associations
     return this.associations;
   * Generate a random key and add it 
   * to an key-to-server association.
   * @return Random Key.
   public static KeyToServerAssociation 
     //Create a new association
     KeyToServerAssociation association 
          = new KeyToServerAssociation();
     //Create the key (UUID)
     association.key = 
               //Convert to string
               //remove the unnecessary dashes
               .replace("-", "");
     //Return the association
     return association;

Finally we have come to the end of the demonstration: the "runner" class that will actually execute our Key-to-Server Association distributed application. At the end of the class, you will find some non-essential functionality that counts the keys for each server. I've done this to demonstrate that the values returning back to the main node are correctly set.

package com.berico.grid;

import java.util.List;

import org.gridgain.grid.GridException;
import org.gridgain.grid.gridify.aop.spring.GridifySpringEnhancer;
import org.gridgain.grid.typedef.G;

 * A more advanced example of 'Gridify'ing 
 * an instance method, manually 'partitioning'
 * the work amongst server nodes and 'reducing'
 * the result back into a single list.
 * The class is the runner for the example.
 * In this scenario, we are associating the keys
 * (UUID's) of hypothetical records to the servers
 * those records would be stored on.  This is a 
 * scenario that happens quite often in distributed
 * database (we use 8 Redis instances, for example). 
 * @author Richard C. (Berico Technologies)
 public final class KeyAssociationExample {
   //Ensure this can't be instantiated
   private KeyAssociationExample(){}
   * Entry-point into our example
   * @param args Command Line Arguments
   * @throws GridException
   public static void main(String[] args) throws GridException{
     //Reference to the key-to-server associations
     List<KeyToServerAssociation> associations;
     //Start a new grid node instance
     //Wrap the distributed call so we can stop
     //the server node whether we are successful
     //or not.
     try {
       //Create a new instance of the
       //Test Data Generator
       TestDataGenerator provider 
            = new TestDataGenerator();
       //Generate some test data, exactly
       //1,000,000 keys
       //Create a new instance of the
       //Bulk Association Builder.  We are
       //going to use a Lowest Order Byte(s)
       //strategy for partitioning our keys.
       IBulkAssociationBuilder associationBuilder 
            = new LobUuidAssociationBuilder(
       //We are going to use Spring AOP to advice
       //the builder.  If you use AspectJ or 
       //JBoss AOP (to compile-time weave the 
       //advice onto the class), this is
       associationBuilder = GridifySpringEnhancer
       //With our newly enhanced Association
       //Builder, let's build the associations...
       //of course, we're going to do this
       //across a grid of computers.
       associations = associationBuilder
     finally {
       //Stop the node regardless of whether
       //we are successful.
     //We'll wrap up by demonstrating that the 
     //distributed operation was successful.
     //To prove it was, we will count the number of
     //keys associated with each server.
     //Yes I know this is heinously inefficient!
     //Iterate over the number of servers
     for(int i = 0; 
             i < LobUuidAssociationBuilder.NUMBER_OF_SERVERS;
       //Set a counter
       int count = 0;
       //Iterate over the associations
       for(KeyToServerAssociation association : associations){
         //if the current association's server is the
         //same as the iteration, increment the
         if(association.server == i){
       //Finally, print the results to the console.
                "Server %s had %s keys associated with it.", 
                i + 1, count));

And here is the results from the node that tasked the grid (Eclipse):

[23:29:29]    _____     _     _______      _         ____  ___    
[23:29:29]   / ___/____(_)___/ / ___/___ _(_)___    |_  / / _ \  
[23:29:29]  / (_ // __/ // _  / (_ // _ `/ // _ \  _/_ <_/ // /  
[23:29:29]  \___//_/ /_/ \_,_/\___/ \_,_/_//_//_/ /____(+)___/
[23:29:29]        ---==++ C0MPUTE + DATA + CL0UD ++==---
[23:29:29]                 ver. 3.0.3c-20122010
[23:29:29]    2005-2010 Copyright (C) GridGain Systems, Inc.
[23:29:29] Quiet mode.
[23:29:29]   ^-- To disable add -DGRIDGAIN_QUIET=false or "-v" to ggstart.{sh|bat}
[23:29:29] << Community Edition >>
[23:29:29] Language runtime: Java Platform API Specification ver. 1.6
[23:29:29] GRIDGAIN_HOME=null
[23:29:31] Node JOINED [nodeId8=71f4cdf5, addr=[], CPUs=2]
[23:29:31] '--ToPoLoGy SnApShOt [nodes=3, CPUs=2, hash=0xD3C8011B]
[23:29:31] Node JOINED [nodeId8=385a77d4, addr=[], CPUs=2]
[23:29:31] Node JOINED [nodeId8=2709d99f, addr=[], CPUs=2]
[23:29:31] '--ToPoLoGy SnApShOt [nodes=4, CPUs=2, hash=0x5B332E38]
[23:29:34] JVM: Sun Microsystems Inc., Java(TM) SE Runtime Environment ver. 1.6.0_23-b05
[23:29:34] OS: Windows 7 6.1 amd64, rclayton
[23:29:34] VM name: 21164@deathstar
[23:29:34] Local ports: TCP:47103 UDP:47200 TCP:47303 
[23:29:34] GridGain started OK [grid=default, nodeId8=22aaaa77, CPUs=2, addrs=[]]
[23:29:34] ZZZzz zz z...
Key associated with server: [Key: 4f22c46355b24050ab09267bf26000fd Server: 3]
Key associated with server: [Key: e733cf098fa14fb6898eee97c653ca2b Server: 9]

Abbreviated...You're missing about 250,000 entries.

Key associated with server: [Key: 8e44a025efb74d5f8b45c23c781c6128 Server: 5]
Key associated with server: [Key: d93e7742bf5945f5b534e6e9e8f9c251 Server: 2]
Key associated with server: [Key: 27ae198b30644d0ab9ba806ed93b55f4 Server: 11]
[20:46:58] GridGain stopped OK [uptime=00:01:47:435]
Server 1 had 62502 keys associated with it.
Server 2 had 62367 keys associated with it.
Server 3 had 62479 keys associated with it.
Server 4 had 62753 keys associated with it.
Server 5 had 62526 keys associated with it.
Server 6 had 62139 keys associated with it.
Server 7 had 62560 keys associated with it.
Server 8 had 62657 keys associated with it.
Server 9 had 62404 keys associated with it.
Server 10 had 62325 keys associated with it.
Server 11 had 62818 keys associated with it.
Server 12 had 62285 keys associated with it.
Server 13 had 62129 keys associated with it.
Server 14 had 62292 keys associated with it.
Server 15 had 63142 keys associated with it.
Server 16 had 62625 keys associated with it.

I took a screen shot to demonstrate what the task looks like on 4 local nodes started on my laptop.

Full View: http://dl.dropbox.com/u/12311372/KeyAssociationExample.Output.PNG

And I'm spent! I hope you found this introduction to GridGain rewarding. I highly recommend the framework to any engineer considering a "scale out" or "scale up" of their system. In a later post, I will introduce GridGain's functional API, making the framework function eerily similar to Microsoft's Task Parallel Library.

Good Luck and Happy Coding.

Richard C.


Eclipse Project:  http://dl.dropbox.com/u/12311372/GridifyWithGridGain.zip
Image 1:  http://dl.dropbox.com/u/12311372/GridifyExample.Output.PNG
Image 2:  http://dl.dropbox.com/u/12311372/RunnableExample.Output.PNG
Image 3:  http://dl.dropbox.com/u/12311372/KeyAssociationExample.Output.PNG


Terracotta:  http://www.terracotta.org/
Apache Hadoop:  http://hadoop.apache.org/
GridGain:  http://www.gridgain.com
Performance Comparison of Hadoop, GridGain and Hazelcast: http://java.dzone.com/articles/comparison-gridcloud-computing-0


  1. Richard,
    Thanks a lot for the excellent post. Fantastic effort!

    Nikita Ivanov.
    GridGain Systems.

  2. Nikita,

    Thank you for your comments. Would you mind if I post the GridGain logo at the top of this post?

    Thanks again,


Note: Only a member of this blog may post a comment.