Developing distributed applications in JavaScript

From Web Computing Documentation
Jump to: navigation, search

Contents

Introduction

The framework is compiled to JavaScript and is meant to run JavaScript applications in the browser. When creating such applications you have to use the functions of the workunit API to receive inputs, report progress, save results and checkpoints and retrieve checkpoints.

The general structure of these applications are

  1. Retrieve potential checkpoint data
  2. If no checkpoint exists start from beginning
  3. Do the math
    1. Save checkpoints
    2. Report progress regularly
    3. Write results


Workunit structure.png


Once the execution of the application ends, the framework uploads the requested results to the work source. The framework doesn't use nor require setting the exit status but uploads it to the work source when needed.

Examples

The following application retrieves a list of numerical pairs and calculates their greatest common divisor. The work source sends the job in a file containing a JSON string. The string represents the object holding the pairs. The application calculates the GCD for one pair at a time then it saves the result, makes a checkpoint, reports the progress then moves on to the next pair.

In our example the work source is a BOINC server, that sends workunits with a logical input name of pairs and expects a result named GCD.

var pairsJson = js.input.read("pairs");         //reading the input data of the workunit
try
{
    var pairs = js.evalJson(pairsJson);         //parsing JSON data
}
catch(e)
{
    return -1;                                  //if JSONinput is not valid
}
 
var checkpoint = js.work.getCheckpoint();       //retrieving the checkpoint if exists
 
if(checkpoint == null)
{
    var step = 0;                               //the number of the pair we are working on
    checkpoint = {};
}
else
    var step = checkpoint.step;
 
var stepResult;
for(var i=step+1; i<pairs.numPairs; i++)
{
    stepResult = GCD(pairs.data[i].a, pairs.data[i].b);
    js.output.writeLine("GCD", stepResult);
    js.work.setProgress(i/pairs.numPairs);      //report what portion of the job is done
    if(i%10 == 0)
    {
        checkpoint.step = i;
        js.work.setCheckpoint(checkpoint);      //after every 10 elements save checkpoint
    }
}
 
return 0;                                       //exit status, line can be omitted
 
//*************************************
// Euclidean algorithm (doing the math)
//*************************************
 
function GCD(a, b)
{
    while(b != 0)
    {
        t = b;
        b = a%b;
        a = t;
    }
    return a;
}

The input string recieved from the BOINC server and read by js.input.read looks like this

{
    "numPairs": 5,     //the total number of pairs contained within this input
    "data":
    [
        {
            "a": 236123,
            "b": 58293481
        },
        {
            "a": 6876241,
            "b": 22543
        },
        {
            "a": 64571,
            "b": 98754
        },
        {
            "a": 112,
            "b": 6343
        },
        {
            "a": 89761243,
            "b": 234187
        }
    ]
}

This example demonstrates the usage of all the essential functions of the framework. You can see a detailed explanation of the functions in the Workunit API documentation

Testing

For testing purposes you can use the jsworker command line tool. jsworker provides Web Computing functionality in a console environment, so that you can easily test your workunit code without having to deploy the executables on a web server. The tool requires two arguments.

jsworker program.js input.js

The first argument is your workunit executable. You can use every WC function so applications tested successfully can be deployed on a web server without any modification.

The second argument is a special executable file responsible for generating the required inputs for the workunit executable. It can use the special js.input.write(key, value) function that is not part of the Web Computing Framework. Under operational conditions the inputs would be downloaded from the work distributor and handed over by the WCF. When testing you need to provide them yourself using this function.

Example

In the example I will use the previously introduced program searching the greatest common divisor of numerical pairs. The code above will be saved as GCD.js without any modification.

In order to test this code with the command line tool, we need to create an executable that will generate the input data. As explained above, GCD.js will expect an input called pairs.

js.input.write("pairs", '{"numPairs": 2,"data":[{"a": 236123,"b": 58293481},{"a": 6876241,"b": 22543}]}');

Once this code is saved as GCDinput.js you can test your workunit.

$jsworker GCD.js GCDinput.js

#Checkpoint: {"step":0}
#Progress: 50%
==== The workunit has returned with 0 =======
==== "GCD" start ============================
1
1

==== "GCD" end   ============================

The tool will display any output stream you used on stdout at the end. You can also use js.work.debug(string) to display messages during the lifetime of your workunit. You can use js.work.setCheckpoint in the second executable to test checkpoint recovery.

Deploying your application

Once your code is ready you need to save it as simple .js files. You will most likely want to generate the input files by a program. They can be saved as .js files as well, then both the application executable and the input files must be uploaded to a work distribution system. You can read more about deploying a JS application on BOINC here.

Guidelines

When developing scientific applications in JavaScript you should pay attention to some of the limitations of the language.

Numbers in JavaScript

JavaScript is an untyped language. Depending on the definition of variables they can be either string, number or boolean. There are other special variable types as well like function or undefined.

All numbers are stored as double precision floating point numbers. Apart from the special numbers (NaN) JavaScript complies with the IEEE-754 standard. This means that as long as you are working with finite floats they should behave exactly like they do in most typed languages like C.

A double can represent integers with an absolute value up to 2^53-1 accurately. In any case you shouldn't have problems using 32 bit integers, but you should be aware that integers in JavaScript don't overflow. If you need that behaviour use modulo operation.

References

When using the equality operator (=) with object identifiers JS will equate the identifiers references. You should pay attention to this behaviour.

var object1 = {};
var object2 = {};
var object3 = {};
 
object1.value = 1;
object2 = object1;               //object2 now references object1
object3.value = object1.value;   //object3 now holds the same value but not the same reference
Personal tools
Modules