Sunday, June 2, 2013

Introducing Node

Why did Node come ?

To understand why Node came into being, we should first understand what is 
  1. a blocking I/O programming
  2. multi-threaded programming and
  3. event driven programming
Before explaining these 3 different types of programming and comparing their performances, let me explain to you what these programming types are with an example. 

So, Ms.Dimple Gusain is at the south asia's largest mall, Lulu.

Situation: 
  1. She among many others are waiting outside the Lulu entrance to get in. 
  2. Among these are people who have won movie tickets from an online lot conducted by the Lulu group, people who have come to shop and people who have come because they did not know anything better to do.
  3. Dimple too has won a ticket to this horror movie. She goes nuts about horror movies.
  4. Everyone needs to pay to get inside, except for the people who have won the movie tickets. Dimple is excited.

Blocking I/O programming case: 
  1. There is just one counter and one queue. 
  2. In the counter are 2 systems, one for giving away the entrance tickets and one for booking the movie tickets, the seat management system(SMS).
  3. The ticket counter starts issuing tickets to the people in the queue, one by one.
  4. The entrance tickets are issued fast but the movie tickets are not. Lets see why.
  5. Dimple shows up and produces the movie ticket.
  6. The counter checks if its a valid number. The SMS confirms it is.
  7. Next the SMS checks the available seats and allocates one. 
  8. The SMS then updates the seats status, nullifies her ticket, registers her presence and notifies the account system.
  9. All the while this is happening, the queue is waiting impatiently for tickets. They are all on the verge of cursing Dimple but she being the chosen one, escapes unhurt.
  10. Another prize winner, Supi, shows up. The painful wait for the queue continues but he might not make it into the theatre. Sad but true.

Multi-threaded programming case:

  1. There is 1 counter per person. Not the ideal mall but lets just say Lulu takes customer services to the highest level.
  2. The ticker counters starts issuing tickets in all counters but looks like the wait at each counter could still be optimized. Why? here is why:
    1. All the counters are connected to the seat management system but at any given point in time, only one will have access to it. 
    2. This means that if counter 1 is using it to book a seat, then counter 2(although in the process of issuing movie tickets) will be locked out from accessing the seat management system. This is to prevent accidental allocation of the same seat to Dimple and Supi. Supi doesn't mind sharing it with Dimple,  although but vice versa may not be true.
    3. Only after the counter 1 books the seats and removes the locks, does the counter  2 get to access and book the seats.
    4. Thus this whole process could be summarized as follows:
      1. counter 1 starts booking.
      2. It requests control of the seat management system(SMS).
      3. The SMS verifies that it is not presently locked.
      4. Upon confirmation, it gives control to counter 1 which places a lock on it and proceeds to book the seats.
      5. counter 2 starts booking. So do counters 4 and 10.
      6. All of them requests control of the SMS.
      7. The SMS verifies and declines the request.
      8. counter 2 places the booking in queue and waits. so do counters 4 and 10.
      9. counter 1 finishes booking but now has to update all the other systems about the booking as mentioned in the above case point 6.
      10. Since all the other updates do not concern the SMS, counter 1 releases the lock on it.
      11. counter 2 being first in the queue, this time proceeds to lock the SMS and book the seats.
      12. counters 5 and 6 starts booking and request the control of SMS.
      13. SMS declines since counter 2 is using it.
      14. After counter 2 release the locks, counter 4 gets to use it and the cycle repeats for all the counters in the queue.
    5. There is a huge improvement over the previous case when considering the fact that entrance tickets can now be issued fast, without waiting for the movie ticket process to finish.
    6. However in the case of movie tickets, The time taken to verify if the seat management system is in use and then placing the locks, storing the next request in queue, releasing the locks, getting the next request from queue and then repeating all over again - - Although this case is an improvement over the previous case, this is all still wasting time when considering the movie ticket booking alone. 
    7. Thus on careful study, it is revealed that when multiple counters access the same data, they all have to wait and do it in queue. If this was the case then why did we have multiple counters in the first place?

Event driven programming case:

  1. There is just one counter and one queue.
  2. But the ticket counter functions in a different manner. 
  3. Suppose there are 6 persons in the queue. "A", "B", "Dimple", "C", "Supi" and "D".
  4. Dimple and Supi are going for the movie but others are going to shop.
  5. Counter takes the money from "A", and entrance ticketing system being fast, issues him the entrance ticket right away. 
  6. Counter then accepts money from "B" and follows the same procedure.
  7. Its Dimple's turn next. Counter gets the movie coupon from her and passes it to SMS to confirm it. Meanwhile Dimple waits around patiently(although on reality, waiting and patience is not for Dimple)
  8. Counter then accepts money from C and issues the entrance tickets.
  9. Counter then accepts movie coupon from Supi.
  10. The SMS has now finished booking seats for Dimple and notifies the counter of the seat number with the ticket.
  11. Counter makes Supi wait, issues the ticket to Dimple and continues to process Supi's ticket by passing it to SMS.
  12. Counter then accepts money from D and issues the entrance tickets.
  13. Counter is notified of the seat number with ticket by the SMS for Supi.
  14. Counter stops its current activity and issues the tickets to Supi. Supi is excited as he gets a seat near to Dimple. All well.
  15. Thus in this case, the SMS notifies the counter whenever there is a ticket-ready-event, who otherwise can keep servicing the requests of the customers without blocking them.
  16. When notified the counter can issue the tickets and return to servicing the customers again.
  17. Since there are no locks or other overheads, there is significant time gained for the small wait that the customers have to do when issuing the tickets.

In the software world, the networked applications like the ones on the internet had many concurrent requests to be serviced at the same time usually for some operations on a common data. Multi-threading was a great alternative but soon had to face difficulties due to the overheads in locks and semaphores to sync the data access. The problem with these were that it was tremendously difficult to locate bugs if the developer had somehow missed out on locks or changing contexts for individual threads.

With the growing power of the CPUs and faster compilers and interpreters, the perpetuators of network programing once again resorted to the single process but an event-driven model, where the small wait is compensated for the complex locks and context changes in the multi-threaded environment. 

Moreover the only true multi-threading is when there are multiple CPU cores, with each core dedicated to a single process. In all the other cases, the CPU clock is being shared by the individual processes which means at any given point in time there is just one process being run.

Ryan Dahl and Event Driven Programming 

Ryan Dahl, the creator of Node had worked with Ruby as well as its framework Rails and had found it to be insufficient in handling application that had huge concurrent service requests. He was particularly upset by the I/O blocking programming style implemented in it. He then set out to create an event driven model for networked applications and was searching for a good language to implement it. He tried C but soon stopped due to the complexity in maintaining contexts for multiple processes. He then tried Lua, but gave up because Lua already had a lot of I/O blocking libraries along with non-blocking ones. This would cause confusion in the mind of developers.

2 important things for implementing an event driven model was that 
  • firstly, the functions needed to be passed as arguments as consider this example:target.on_some_event("event_name", fire this callback function);as you can see, the target.on_some_event is a function that is fed with 2 arguments, namely a string "event_name" and a function. In an I/O blocking programming model, there is no need for the function to be passed as an argument.  You directly invoke it.
    A sample code could explain this -

    I/O blocking style:

    var result = query('SELECT * FROM posts WHERE id = 1');
    do_something_with(result); // direct invocation

    Event driven style:
    query_finished = function(result){
          do_something_with(result);
    }
    query('SELECT * FROM posts WHERE id = 1', query_finished);

  •  secondly, the functions needed to remember the context in which they were declared. This is known as closures.
    consider the following example:
    (function(){
    var count = 0;
    target.on_some_event("event_name", fire this callback function(){
        count++;
    });
    })();
    By the time the callback function would have been called, the parent function may already have finished execution. Yet the callback function has access to the variables in its parent function(this is known as remembering its context).
Event Loop

Event driven programming styles are also accompanied by an event loop. An event loop is a construct that runs an infinite loop checking for events and triggering their handlers. 
Event loop is a parallel thread running alongside the main app thread, thus raising eyebrows on the statement that Node is a single threaded process. Well, the statement is not w.r.to the internal implementation but more in lines with the way that Node handles requests in a networked application.
Thus any language that implements an event loop can be used for event driven programming. e.g. Ruby's Event Machine, Perl's AnyEvent and Python's Twisted.

What Ryan noticed

Ryan noticed that javascript had functions as first-class objects(that they can be passed as arguments) and also had closures. This made it a perfect fit to implement the event driven model for networked applications.


Javascript was an output of a race by Netscape people with Microsoft, as the latter was getting ready to release their first browser into the market first. The language was thus not perfect and had some cleaning to be done. Luckily for Ryan, Google come up with V8, a powerful javascript engine created to drive its insanely successful browser, Chrome. 

What Ryan did

Ryan built a library of low level APIs that facilitated the creation of networked applications including building servers, sockets etc. He must have built these in C.
He then built a platform using Javascript(V8 powered):

  • to access these low level APIs
  • to create networked apps in an event driven model using callbacks and closures 
It is this platform that is now called Node.


Friday, May 31, 2013

Installing Node and Node Package Manager

What is Node good at?

Node allows us to easily build fast and scalable networked applications. Every technology emerges out of a reason and node is the answer to networked apps like chat applications which have high concurrent usage scenarios.

Why all the buzz?

Node has been widely adopted due to the following reasons:
  1. program in javascript - widely popular technology plus a strong engine in the form of V8 to back it up
  2. simplicity - core functionality is kept at a minimum. In case you need more functionality, several third party plugins are made available. Node Package Manager(NPM) also manages the dependency issues for you. Its hell easy!
  3. Installs easily on Windows, Linux, Mac and Solaris

Installation

Follow the installation procedures at http://nodejs.org to install the latest stable version on your PC. The unstable versions are battlegrounds for the Node Core development team to test out various new features. Unless you want any of the new features, it is recommended to use stable versions for production. Even minor version packages represent stable builds.

Why Node Package Manager?

Using Node alone would be tedious in building huge applications as the language features and the core low-level APIs dont account for a lot of flexibility. 
By low-level APIs we mean for e.g. say manipulating data packets in the TCP data packets, opening and closing sockets etc
Thus for Node, there will need to be built several modules before you can actually start on a project. Waste of time! 

Luckily today, there are package managers for technology platforms that keep track of a public repository of third party modules and plugins, download and manage them on the local machine during the development phase and also manage the dependency issues for various versions. For Node, this is done by the Node Package Manager. NPM provides command line tools to download, install and manage these plugins.
The central repository of 3rd party plugins may be found at http://search.npmjs.org
Since Node 0.6.0 the NPM is included with the Node installer so no separate installs!

More about NPM

NPM installs the 3rd party modules in 2 locations depending on the installation command specified - locally or globally.
Locally - is within the app directory. For e.g. /home/user/apps/dimple_supi/node_modules
Local installation is the default installation place for Node when you type say 
$ npm install dimple
Globally - is within the default install location of Node. For e.g. /usr/local/lib/node_modules . Modules installed here would be available for all the projects and are usually used for command line utilities like express etc. For installing a module globally, precede the module name with -g:
$ npm install -g dimple

When an expression such as var supi = require('dimple'); is encountered in the app js file, Node will first prefer to look up the module locally before proceeding to look it up in the global space. Thus modules installed locally would precede over the ones installed globally.

A few useful NPM commands

$ npm install dimple@">=0.1.0 <0.3.1" - installs a version of dimple that is greater than 0.1.0, nearer to 0.3.1 but not equal to or greater than it. So between dimple@0.2.2 and dimple@0.2.3, the npm would choose dimple@0.2.3 as its nearer to 0.3.1
$ npm install dimple@0.2.x - installs the latest release in 0.2 branch
$ npm uninstall dimple - uninstalls dimple from the local directory. Can be used to remove global installations as well.
$ npm update -g dimple - updates the module to the most latest release. Can be used locally as well.
$ npm update dimple@0.2.x - you should know by now ;)

Using the Executables

Some modules contain one or more executable files. For global installation of such modules, the PATH variable is set to contain this executable and the default installation directory of the executable is /usr/local/bin.
In the case of local installation of such modules, the executables can be located within the node_modules directory with the app directory. For e.g.  ./node_modules/.bin

Resolving dependencies

NPM automatically fetches and installs any modules that the current module depends upon. For e.g.
$ npm install nano

output:
nano@0.9.3 ./node_modules/nano
 ---- supi@1.1.7
 ---- dimple@2.1.1

Dependencies are managed by package.json, a JSON file in the root directory of the app.
The Node modules were initially conceived to be public packages and not private applications. Therefore the name and description attributes in the package.json file were mandatory and they still are. However you can now choose not to make the modules public.
The package.json usually contain the following - name, version, description, authors, repository, contacts, dependencies. Here is an example:
{
        "name":"supi_dimple",
        "version":"1.0.0",
        "dependencies":{
                   "sex":"1.1.x",
                   "dimple":"*",
                   "supi":">2.3.1"
              }
}

That's all folks!