├── examples
├── analytics
│ ├── 5SecondNoOp.R
│ ├── rtScore.R
│ └── insurModel.rData
├── config.json
├── tutorial
│ ├── pooled
│ │ ├── pooled-simulation.js
│ │ ├── pooled-simulation.html
│ │ ├── pooled-basics.js
│ │ └── pooled-basics.html
│ ├── background
│ │ ├── background-basics.js
│ │ └── background-basics.html
│ ├── discrete
│ │ ├── discrete-basics.js
│ │ └── discrete-basics.html
│ └── simulations
│ │ ├── browser
│ │ └── scoring-engine-simulation.js
│ │ └── nodejs
│ │ └── scoring-engine-simulation.js
└── utils
│ └── rbroker-stats-helper.js
├── test
├── config.json
├── README.md
├── fixtures
│ ├── pool.js
│ ├── discrete.js
│ ├── common.js
│ └── job.js
└── specs
│ ├── discrete-task-broker-test.js
│ └── background-task-broker-test.js
├── .gitignore
├── LICENSE.md
├── gulpfile.js
├── gulp
├── util
│ ├── errors.js
│ └── script-filter.js
├── tasks
│ ├── default.js
│ ├── build.js
│ ├── start.js
│ ├── jshint.js
│ ├── uglifyjs.js
│ ├── connect.js
│ ├── browserify.js
│ └── header.js
├── config.js
└── index.js
├── lib
├── task
│ ├── pooled-task.js
│ ├── background-task.js
│ ├── discreate-task.js
│ └── rtask.js
├── rtask-type.js
├── worker
│ ├── rbroker-worker.js
│ ├── background-task-worker.js
│ ├── pooled-task-worker.js
│ └── discrete-task-worker.js
├── util
│ └── rtask-queue.js
├── engine
│ ├── discrete-task-broker.js
│ ├── background-task-broker.js
│ ├── pooled-task-broker.js
│ └── rbroker-engine.js
└── rtask-token.js
├── package.json
├── SECURITY.md
├── README.md
└── rbroker.js
/examples/analytics/5SecondNoOp.R:
--------------------------------------------------------------------------------
1 | Sys.sleep(5)
2 |
--------------------------------------------------------------------------------
/examples/analytics/rtScore.R:
--------------------------------------------------------------------------------
1 | x
2 | customerid
3 | Sys.sleep(1)
4 | score <- customerid * 10
--------------------------------------------------------------------------------
/examples/analytics/insurModel.rData:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/js-rbroker-framework/HEAD/examples/analytics/insurModel.rData
--------------------------------------------------------------------------------
/test/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "endpoint": "http://localhost:8050",
3 | "credentials": {
4 | "username": "testuser",
5 | "password": "YOUR_TESTUSER_PASSWORD"
6 | },
7 | "cors": true,
8 | "allowSelfSignedSSLCert": true
9 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore build
2 | dist/
3 |
4 | # Ignore extensions
5 | *.diff
6 | *.err
7 | *.log
8 | *.orig
9 | *.rej
10 | *.swo
11 | *.swp
12 | *.vi
13 | *.zip
14 | *~
15 |
16 | # Ignore OS or Editor folders
17 | ._*
18 | .cache
19 | .DS_Store
20 | .project
21 | .settings
22 | .idea
23 | .tmproj
24 | *.esproj
25 | *.sublime-workspace
26 | *.sublime-project
27 |
28 | # Ignore node
29 | node_modules/
30 | npm-debug.log
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (C) 2010-2016, Microsoft Corporation
2 |
3 | This program is licensed to you under the terms of Version 2.0 of the
4 | Apache License. This program is distributed WITHOUT
5 | ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
6 | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
7 | Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
8 | details.
--------------------------------------------------------------------------------
/examples/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "host": "http://localhost:8050",
3 | "cors": true,
4 | "credentials": {
5 | "username": "testuser",
6 | "password": "YOUR_TESTUSER_PASSWORD"
7 | },
8 | "constants" : {
9 | "TUTORIAL_REPO_OWNER": "testuser",
10 | "TUTORIAL_REPO_DIRECTORY": "tutorial-rbroker",
11 | "TUTORIAL_NOOP_SCRIPT": "5SecondNoOp.R",
12 | "TUTORIAL_RTSCORE_SCRIPT": "rtScore.R",
13 | "TUTORIAL_INSURANCE_MODEL": "insurModel.rData"
14 | }
15 | }
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | require('./gulp');
--------------------------------------------------------------------------------
/test/README.md:
--------------------------------------------------------------------------------
1 | DeployR JavaScript RBroker Framework Unit Tests
2 | ===============================================
3 |
4 | The DeployR JavaScript RBroker Framework ships with a set of unit tests.
5 |
6 | ```
7 | $ npm test
8 | ```
9 |
10 | By default, the build configuration assumes an instance of the DeployR server
11 | is running on `localhost`. If your instance of DeployR is running at some
12 | other IP address then please udpate the `endpoint` property in the
13 | config.json configuration file as appropriate.
14 |
--------------------------------------------------------------------------------
/test/fixtures/pool.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | module.exports = require('./common');
--------------------------------------------------------------------------------
/test/fixtures/discrete.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | module.exports = require('./common');
--------------------------------------------------------------------------------
/gulp/util/errors.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var gutil = require('gulp-util');
13 |
14 | module.exports = function(err) {
15 | gutil.log(gutil.colors.green(err));
16 | };
--------------------------------------------------------------------------------
/gulp/util/script-filter.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var path = require('path');
13 |
14 | module.exports = function(name) {
15 | return /(\.(js)$)/i.test(path.extname(name));
16 | };
--------------------------------------------------------------------------------
/gulp/tasks/default.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var gulp = require('gulp');
13 |
14 | /*
15 | * Task: default
16 | *
17 | * The default gulp task for deployr.
18 | */
19 | gulp.task('default', ['build']);
--------------------------------------------------------------------------------
/gulp/tasks/build.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var gulp = require('gulp');
13 |
14 | /*
15 | * Task: build
16 | *
17 | * The main build task to prepare the deployr source for browser environments.
18 | */
19 | gulp.task('build', ['jshint', 'header']);
--------------------------------------------------------------------------------
/gulp/config.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var path = require('path'),
13 | pkg = require('../package.json');
14 |
15 | module.exports = {
16 | port: '8080',
17 | root: path.resolve('./'),
18 | dist: './browser',
19 | name: pkg.name,
20 | pkg: pkg
21 | };
--------------------------------------------------------------------------------
/gulp/index.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var fs = require('fs'),
13 | whitelist = require('./util/script-filter'),
14 | tasks = fs.readdirSync('./gulp/tasks/').filter(whitelist);
15 |
16 | tasks.forEach(function(task) {
17 | require('./tasks/' + task);
18 | });
--------------------------------------------------------------------------------
/gulp/tasks/start.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var gulp = require('gulp');
13 |
14 | /*
15 | * Task: start
16 | *
17 | * Runs the build and starts the `Connect` HTTP server for viewing all the
18 | * ./examples HTML samples.
19 | */
20 | gulp.task('start', ['connect']);
21 |
--------------------------------------------------------------------------------
/gulp/tasks/jshint.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var gulp = require('gulp'),
13 | jshint = require("gulp-jshint"),
14 | config = require('../config');
15 |
16 | /*
17 | * Task: jshist
18 | *
19 | * Lint's the entire `deployr` source.
20 | */
21 | gulp.task('jshint', function () {
22 | return gulp.src([config.name + '.js', './lib*.js'])
23 | .pipe(jshint({lookup: true}))
24 | .pipe(jshint.reporter('default'));
25 | });
26 |
--------------------------------------------------------------------------------
/lib/task/pooled-task.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var Base = require('selfish').Base,
13 | RTaskType = require('../rtask-type'),
14 | RTask = require('./rtask');
15 |
16 | module.exports = Base.extend(RTask, {
17 |
18 | initialize: function(props) {
19 | RTask.initialize.call(this, props, RTaskType.POOLED);
20 | },
21 |
22 | toString: function() {
23 | return 'PooledTask: ' + RTask.toString.call(this);
24 | }
25 | });
--------------------------------------------------------------------------------
/lib/task/background-task.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var Base = require('selfish').Base,
13 | RTaskType = require('../rtask-type'),
14 | RTask = require('./rtask');
15 |
16 | module.exports = Base.extend(RTask, {
17 |
18 | initialize: function(props) {
19 | RTask.initialize.call(this, props, RTaskType.BACKGROUND);
20 | },
21 |
22 | toString: function() {
23 | return 'BackgroundTask: ' + RTask.toString.call(this);
24 | }
25 | });
--------------------------------------------------------------------------------
/lib/task/discreate-task.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * CCopyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var Base = require('selfish').Base,
13 | RTaskType = require('../rtask-type'),
14 | RTask = require('./rtask');
15 |
16 | module.exports = Base.extend(RTask, {
17 |
18 | initialize: function(props) {
19 | RTask.initialize.call(this, props, RTaskType.DISCRETE);
20 | },
21 |
22 | toString: function() {
23 | return 'DiscreteTask: ' + RTask.toString.call(this);
24 | }
25 | });
26 |
--------------------------------------------------------------------------------
/lib/rtask-type.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var Base = require('selfish').Base,
13 | Enum = require('enum'),
14 | taskEnum = new Enum(['DISCRETE', 'POOLED', 'BACKGROUND'], 'RTaskType');
15 |
16 | /**
17 | * Defines the currently supported set of `RTask`.
18 | *
19 | * @module rtask-type
20 | * @for rbroker
21 | */
22 | module.exports = Base.extend({
23 | /**
24 | * Discrete task.
25 | */
26 | DISCRETE: taskEnum.DISCRETE,
27 |
28 | /**
29 | * Pooled task.
30 | */
31 | POOLED: taskEnum.POOLED,
32 |
33 | /**
34 | * Background task.
35 | */
36 | BACKGROUND: taskEnum.BACKGROUND
37 | });
--------------------------------------------------------------------------------
/gulp/tasks/uglifyjs.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var gulp = require('gulp'),
13 | uglify = require('gulp-uglify'),
14 | plumber = require('gulp-plumber'),
15 | rename = require("gulp-rename"),
16 | onError = require('../util/errors'),
17 | config = require('../config');
18 |
19 | /*
20 | * Task: uglifyjs
21 | */
22 | gulp.task('uglify', ['browserify'], function() {
23 | return gulp.src(['./browser/' + config.name + '.js'])
24 | .pipe(plumber({ errorHandler: onError }))
25 | .pipe(uglify())
26 | .pipe(rename({
27 | extname: '.min.js'
28 | }))
29 | .pipe(gulp.dest('./browser/'));
30 | });
--------------------------------------------------------------------------------
/lib/worker/rbroker-worker.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var Base = require('selfish').Base,
13 | D = require('d.js');
14 |
15 | module.exports = Base.extend({
16 | initialize: function initialize(task) {
17 | this.task = task;
18 | this.defer = D();
19 | },
20 |
21 | work: function(resourceToken) { /* override */ },
22 |
23 | terminate: function(interrupt) { /* override */ },
24 |
25 | isPending: function() {
26 | return this.defer.promise.isPending();
27 | },
28 |
29 | resolve: function(result) {
30 | this.defer.resolve(result);
31 | },
32 |
33 | reject: function(err) {
34 | this.defer.reject(err);
35 | }
36 | });
37 |
--------------------------------------------------------------------------------
/gulp/tasks/connect.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var gulp = require('gulp'),
13 | connect = require('gulp-connect'),
14 | config = require('../config');
15 |
16 | /*
17 | * Task: connect
18 | *
19 | * Run a webserver for viewing ./examples HTML samples (with LiveReload)
20 | */
21 | gulp.task('connect', ['build'], function() {
22 | connect.server({
23 | root: [config.root + '/examples/', config.dist],
24 | port: config.port,
25 | livereload: true
26 | });
27 |
28 | // Watch JS
29 | gulp.watch([config.name + '.js', './lib/**/*.js'], ['build']);
30 |
31 | // Watch Examples that use HTML and livereload
32 | gulp.watch('./examples/**/*.html', ['build']);
33 | });
--------------------------------------------------------------------------------
/gulp/tasks/browserify.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var browserify = require('browserify'),
13 | gulp = require('gulp'),
14 | source = require('vinyl-source-stream'),
15 | plumber = require('gulp-plumber'),
16 | onError = require('../util/errors'),
17 | config = require('../config');
18 |
19 | /*
20 | * Task: browserify
21 | *
22 | * Runs `browserify` on the `deployr` source.
23 | */
24 | gulp.task('browserify', function() {
25 | return browserify({ debug: true, standalone: config.name, entries: ['./' + config.name + '.js'] })
26 | .ignore('http')
27 | .bundle()
28 | .pipe(plumber({ errorHandler: onError}))
29 | .pipe(source(config.name + '.js'))
30 | .pipe(gulp.dest(config.dist));
31 | });
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rbroker",
3 | "version": "8.0.5",
4 | "description": "JavaScript RBroker Framework for DeployR",
5 | "keywords": [
6 | "deployr",
7 | "deployr.io",
8 | "rbroker",
9 | "ajax",
10 | "R",
11 | "api",
12 | "simple"
13 | ],
14 | "author": "DeployR - Microsoft Corporation",
15 | "contributors": [{
16 | "name": "Sean Wells"
17 | }],
18 | "repository": {
19 | "type": "git",
20 | "url": "git://github.com/microsoft/js-rbroker-framework.git"
21 | },
22 | "homepage": "http://go.microsoft.com/fwlink/?LinkID=698842",
23 | "scripts": {
24 | "start": "gulp start",
25 | "build": "gulp",
26 | "test": "tape test/specs/*.js",
27 | "test-browser": "browserify test/specs/*.js | testlingify"
28 | },
29 | "devDependencies": {
30 | "browserify": "^13.0.1",
31 | "gulp": "^3.9.1",
32 | "gulp-connect": "^4.1.0",
33 | "gulp-header": "^1.8.7",
34 | "gulp-jshint": "^2.0.1",
35 | "gulp-livereload": "^3.8.1",
36 | "gulp-plumber": "^1.1.0",
37 | "gulp-rename": "^1.2.2",
38 | "gulp-uglify": "^1.5.4",
39 | "gulp-util": "^3.0.7",
40 | "tape": "^4.6.0",
41 | "vinyl-source-stream": "^1.1.0",
42 | "when": "^3.7.2"
43 | },
44 | "dependencies": {
45 | "d.js": "^0.6.0",
46 | "deployr": "8.0.5",
47 | "enum": "^0.2.6",
48 | "merge": "^1.1.3",
49 | "selfish": "^0.3.2"
50 | },
51 | "main": "rbroker.js",
52 | "engines": {
53 | "node": ">= 0.10.0"
54 | },
55 | "license": "Apache-2.0",
56 | "bugs": {
57 | "url": "https://github.com/microsoft/js-rbroker-framework/issues"
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/lib/util/rtask-queue.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var Base = require('selfish').Base;
13 |
14 | var RTaskQueue = Base.extend({
15 | initialize: function (capacity) {
16 | this.capacity = capacity;
17 | this.q = [];
18 | },
19 |
20 | /**
21 | * Inserts the specified element at the tail of this queue if it is possible
22 | * to do so immediately without exceeding the queue's capacity, returning
23 | * ```true``` upon success and ```false``` if this queue is full.
24 | */
25 | offer: function (rtask) {
26 | var accepting = this.size() < this.capacity;
27 |
28 | if (accepting) {
29 | this.q.push(rtask);
30 | }
31 |
32 | return accepting; // True if added False otherwise
33 | },
34 |
35 | /**
36 | * Retrieves and removes the head of this queue.
37 | */
38 | take: function () {
39 | return this.q.shift();
40 | },
41 |
42 | /**
43 | * Retrieves, but does not remove, the head of this queue, or returns `
44 | * ``null`` if this queue is empty.
45 | */
46 | peek: function () {
47 | return this.q[0];
48 | },
49 |
50 | /**
51 | * Returns the number of elements in this queue.
52 | */
53 | size: function () {
54 | return this.q.length;
55 | },
56 |
57 | /**
58 | * Returns ```true``` if this collection contains no elements.
59 | * This implementation returns size() === 0.
60 | */
61 | isEmpty: function () {
62 | return this.size() === 0;
63 | },
64 |
65 | clear: function () {
66 | this.q.length = 0;
67 | },
68 |
69 | iter: function (fn) {
70 | this.q.forEach(fn);
71 | }
72 | });
73 |
74 | module.exports = RTaskQueue;
--------------------------------------------------------------------------------
/lib/task/rtask.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var Base = require('selfish').Base,
13 | merge = require('merge');
14 |
15 | /**
16 | * Represents any R Analytics task for execution on an `RBroker`.
17 | *
18 | * @module rtask
19 | * @for rbroker
20 | */
21 | module.exports = Base.extend({
22 |
23 | /**
24 | * Represents any R Analytics task for execution on an `RBroker`.
25 | *
26 | * @class
27 | * @constructs
28 | * @param {Object} props - The task properties object hash.
29 | */
30 | initialize: function initialize(props, type) {
31 | this.type = type;
32 | this.token = null;
33 |
34 | props = props || {};
35 |
36 | for (var index in props) {
37 | Object.defineProperty(this, index, {
38 | value: props[index],
39 | enumerable: true
40 | });
41 | }
42 | },
43 |
44 | /**
45 | * A clone of the `RTask` DeployR API properties as a flat object.
46 | *
47 | * @returns {Object} A clone of the `RTask` properties.
48 | */
49 | serialize: function() {
50 | var task = {};
51 |
52 | Object.keys(this).filter(function(key) { // blacklist
53 | return (['type', 'token'].indexOf(key) === -1);
54 | })
55 | .forEach(function(key) {
56 | task[key] = this[key];
57 | }.bind(this));
58 |
59 | return merge(true, task);
60 | },
61 |
62 | toString: function() {
63 | var out = '',
64 | keys = Object.keys(this);
65 |
66 | for (var o in keys) {
67 | out += ' [ ' + keys[o] + ' = "' + this[keys[o]] + '" ]';
68 | }
69 |
70 | return out;
71 | }
72 | });
--------------------------------------------------------------------------------
/test/fixtures/common.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var merge = require('merge'),
13 | deployr = require('deployr'),
14 | config = require('../config');
15 |
16 | if (config.allowSelfSignedSSLCert) {
17 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
18 | }
19 |
20 | function createOptions(good) {
21 | //
22 | // Example-Fraud-Score constants.
23 | //
24 | var EFS_DIRECTORY = "example-fraud-score",
25 | EFS_BAD_DIRECTORY = "bad-example-fraud-score",
26 | EFS_MODEL = "fraudModel.rData",
27 | EFS_FUNCTION = "ccFraudScore.R",
28 | EFS_USERNAME = "testuser";
29 |
30 | return {
31 | //
32 | // Loading all files found in EFS_DIRECTORY.
33 | //
34 | preloadbydirectory: good ? EFS_DIRECTORY : EFS_BAD_DIRECTORY,
35 | //
36 | // Loading EFS_FILE found in EFS_DIRECTORY.
37 | // Note, setting this option is redundant here in practical terms as
38 | // the preloadByDirectory would have already loaded the EFS_FUNCTION
39 | // file from the EFS_DIRECTORY. Howvever, this method is testing the
40 | // preload directory mechanism.
41 | //
42 | preloadfilename: EFS_FUNCTION,
43 | preloadfiledirectory: good ? EFS_DIRECTORY : EFS_BAD_DIRECTORY,
44 | preloadfileauthor: EFS_USERNAME,
45 | //
46 | // Loading EFS_MODEL found in EFS_DIRECTORY.
47 | //
48 | preloadobjectname: EFS_MODEL,
49 | preloadobjectdirectory: good ? EFS_DIRECTORY : EFS_BAD_DIRECTORY,
50 | preloadobjectauthor: EFS_USERNAME,
51 | //
52 | // Loading aribtrary R object.
53 | //
54 | rinputs: [deployr.RInput.character('arbitrary', 'testme')]
55 | };
56 | }
57 |
58 | module.exports = merge({
59 | BAD_ENDPOINT: 'http://bad.end.point:999',
60 | BAD_CREDENTIALS: {
61 | username: 'bad-username-12321',
62 | password: 'bad-password-12321'
63 | },
64 | BAD_SCRIPT_NAME: 'DoesNotExist.R',
65 | BAD_DIR_NAME: 'not-a-dir',
66 | BAD_AUTHOR_NAME: 'notauser',
67 |
68 | good: function() {
69 | return createOptions(true);
70 | },
71 |
72 | bad: function() {
73 | return createOptions(false);
74 | }
75 | }, config);
76 |
--------------------------------------------------------------------------------
/lib/engine/discrete-task-broker.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 | var D = require('d.js'),
12 | RBrokerEngine = require('./rbroker-engine'),
13 | DiscreteTaskWorker = require('../worker/discrete-task-worker');
14 |
15 | /**
16 | * The Discrete Task Runtime acquires DeployR grid resources per `RTask`
17 | * on-demand.
18 | *
19 | * @module discrete-broker
20 | * @for rbroker
21 | */
22 | module.exports = RBrokerEngine.extend({
23 | /**
24 | * The Discrete Task Runtime acquires DeployR grid resources per `RTask`
25 | * on-demand.
26 | *
27 | * @class
28 | * @constructor
29 | * @param {Object} [options] Configuration options.
30 | */
31 | initialize: function (config) {
32 | RBrokerEngine.initialize.call(this, config);
33 |
34 | // Initialize the resourceTokenPool with Integer based resourceTokens.
35 | for (var i = 0; i < this.parallelTaskLimit; i++) {
36 | this.resourceTokenPool.offer(i);
37 | }
38 |
39 | if (config.credentials) {
40 | this.ruser = this.validateEndpoint().io('/r/user/login')
41 | .data(config.credentials)
42 | .ctx(this)
43 | .end(function(res) { this.emit('ready'); });
44 | } else {
45 | this.validateEndpoint(function() {
46 | this.ruser = null;
47 | this.emit('ready');
48 | });
49 | }
50 | },
51 |
52 | /**
53 | * Not supported. Support for `refresh` is only available on the
54 | * `PooledTaskBroker` runtime.
55 | *
56 | * @method refresh
57 | * @override
58 | * @api public
59 | */
60 | refresh: function (config) {
61 | throw new Error('DiscreteTaskBroker refresh not supported.');
62 | },
63 |
64 | /**
65 | * @Override
66 | */
67 | createWorker: function (task) {
68 | return DiscreteTaskWorker.new(task, this.ruser);
69 | },
70 |
71 | /**
72 | * Release all client-side and server-side resources maintained by or on
73 | * behalf of an instance of `RBroker`.
74 | *
75 | * @method shutdown
76 | * @override
77 | * @return {Promise} A promise wrapping the resolution of either "resolve" or
78 | * "reject" callback.
79 | * @api public
80 | */
81 | shutdown: function () {
82 | this.flush();
83 |
84 | return this.ruser ? this.ruser.release() : D.promisify(true);
85 | }
86 | });
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Security
4 |
5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
6 |
7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
8 |
9 | ## Reporting Security Issues
10 |
11 | **Please do not report security vulnerabilities through public GitHub issues.**
12 |
13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
14 |
15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
16 |
17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).
18 |
19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
20 |
21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
22 | * Full paths of source file(s) related to the manifestation of the issue
23 | * The location of the affected source code (tag/branch/commit or direct URL)
24 | * Any special configuration required to reproduce the issue
25 | * Step-by-step instructions to reproduce the issue
26 | * Proof-of-concept or exploit code (if possible)
27 | * Impact of the issue, including how an attacker might exploit the issue
28 |
29 | This information will help us triage your report more quickly.
30 |
31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
32 |
33 | ## Preferred Languages
34 |
35 | We prefer all communications to be in English.
36 |
37 | ## Policy
38 |
39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
40 |
41 |
42 |
--------------------------------------------------------------------------------
/lib/worker/background-task-worker.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var Base = require('selfish').Base,
13 | deployr = require('deployr'),
14 | D = require('d.js'),
15 | RTaskType = require('../rtask-type'),
16 | RBrokerWorker = require('./rbroker-worker');
17 |
18 | module.exports = RBrokerWorker.extend({
19 |
20 | initialize: function (task, ruser) {
21 | RBrokerWorker.initialize.call(this, task);
22 | this.ruser = ruser;
23 | this.job = null;
24 | },
25 |
26 | work: function(resourceToken) {
27 | var self = this,
28 | startTime = new Date().getTime(),
29 | data = this.task.serialize(),
30 | rinputs = data.rinputs || [],
31 | routputs = data.routputs || [];
32 |
33 | delete data.rinputs;
34 | delete data.routputs;
35 |
36 | this.resourceToken = resourceToken;
37 |
38 | return deployr.io('/r/job/submit')
39 | .share(this.ruser.getCookies())
40 | .data(data)
41 | .global(false) // supress global error events for this `io`
42 | .rinputs(rinputs)
43 | .routputs(routputs)
44 | .promise()
45 | .then(function(res) {
46 | var about = res.get('job');
47 |
48 | self.job = about.job; // job-id
49 |
50 | // resolve the promise which kicks-off the callback
51 | return {
52 | task: self.task,
53 | result: {
54 | id: about.job,
55 | type: RTaskType.BACKGROUND,
56 | timeOnCode: 0,
57 | timeOnServer: 0,
58 | timeOnCall: (new Date().getTime() - startTime),
59 | success: true,
60 | failure: null
61 | }
62 | };
63 | });
64 | },
65 |
66 | terminate: function(interrupt) {
67 | if (interrupt && this.job) {
68 | this.ruser.io('/r/job/cancel')
69 | .data({ job: this.job })
70 | .end();
71 |
72 | return true;
73 | } else {
74 | //
75 | // RTask still pending confirmation from RBroker if there is no
76 | // `resourceToken` hence can not be interrupted [or] the task is
77 | // being worked on and a forced `interrupt` was not given
78 | //
79 | return false;
80 | }
81 | }
82 | });
83 |
--------------------------------------------------------------------------------
/lib/engine/background-task-broker.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var merge = require('merge'),
13 | RBrokerEngine = require('./rbroker-engine'),
14 | BackgroundTaskWorker = require('../worker/background-task-worker');
15 |
16 | /**
17 | * The Background Task Runtime acquires DeployR grid resources per `RTask`
18 | * based on the server-side management of asynchronous grid resources.
19 | *
20 | * @module brackground-broker
21 | * @for rbroker
22 | */
23 | module.exports = RBrokerEngine.extend({
24 | /**
25 | * The Background Task Runtime acquires DeployR grid resources per `RTask`
26 | * based on the server-side management of asynchronous grid resources.
27 | *
28 | * @class
29 | * @constructor
30 | * @param {Object} [options] Configuration options.
31 | */
32 | initialize: function (config) {
33 | /*
34 | * This limit is set simply to ensure the BackgroundTaskBroker does not
35 | * swamp the server which too many concurrent HTTP requests when submitting
36 | * RTask. The real queueing of RTask is handled by the server, this broker
37 | * simply pushed the RTask into the server-managed queue.
38 | */
39 | var PARALLEL_TASK_LIMIT = 10;
40 |
41 | RBrokerEngine.initialize.call(this, merge(config, {
42 | maxConcurrentTaskLimit: PARALLEL_TASK_LIMIT
43 | }));
44 |
45 | // Initialize the resourceTokenPool with Integer based resourceTokens.
46 | for(var i = 0; i < this.parallelTaskLimit; i++) {
47 | this.resourceTokenPool.offer(i);
48 | }
49 |
50 | this.ruser = this.validateEndpoint().io('/r/user/login')
51 | .data(config.credentials)
52 | .ctx(this)
53 | .end(function(res) { this.emit('ready'); });
54 | },
55 |
56 | /**
57 | * Not supported. Support for `refresh` is only available on the
58 | * `PooledTaskBroker` runtime.
59 | *
60 | * @method refresh
61 | * @override
62 | * @api public
63 | */
64 | refresh: function (config) {
65 | throw new Error('BackgroundTaskBroker refresh not supported.');
66 | },
67 |
68 | /**
69 | * Release all client-side and server-side resources maintained by or on
70 | * behalf of an instance of `RBroker`.
71 | *
72 | * @method shutdown
73 | * @override
74 | * @return {Promise} A promise wrapping the resolution of either "resolve" or
75 | * "reject" callback.
76 | * @api public
77 | */
78 | shutdown: function () {
79 | this.flush();
80 |
81 | return this.ruser.release();
82 | },
83 |
84 | /**
85 | * @override
86 | * @api private
87 | */
88 | createWorker: function (task) {
89 | return BackgroundTaskWorker.new(task, this.ruser);
90 | }
91 | });
92 |
--------------------------------------------------------------------------------
/examples/tutorial/pooled/pooled-simulation.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /*
4 | * Copyright (C) 2010-2016, Microsoft Corporation
5 | *
6 | * This program is licensed to you under the terms of Version 2.0 of the
7 | * Apache License. This program is distributed WITHOUT
8 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
9 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
10 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
11 | * details.
12 | */
13 |
14 | /*!
15 | * usage: $ node pooled-simulation.js
16 | * output: Task execution lifecycle logged to stdout
17 | *
18 | * Example using a PooledTaskBroker.
19 | *
20 | * 1. Prepare 'Pooled Broker Configuration' instance.
21 | *
22 | * This example creates an authenticated `PooledTaskBroker`
23 | * to act as our scoring engine.
24 | *
25 | * The following steps initialize the pool of
26 | * R sessions dedicated to our scoring engine.
27 | *
28 | * In this example, we pre-initialize the workspace
29 | * for each R session in the pool to contain the
30 | * R model used when scoring customer requests.
31 | *
32 | * The R model, insurModel.rData, is loaded from
33 | * the DeployR repository into each workspace at
34 | * startup time. Preloading the R model at startup
35 | * reduces runtime overhead and improves overall
36 | * response times in the application.
37 | */
38 |
39 | var deployr = require('deployr'),
40 | rbroker = require('../../../rbroker'),
41 | config = require('../../config'),
42 | ScoringEngineSimulation = require('../simulations/nodejs/scoring-engine-simulation');
43 |
44 | var DEMO6_TARGET_POOL_SIZE = 10;
45 |
46 | var brokerConfig = {
47 | maxConcurrentTaskLimit: DEMO6_TARGET_POOL_SIZE,
48 | host: config.host,
49 | cors: config.cors,
50 | credentials: config.credentials,
51 | releaseGridResources: false,
52 | logging: false,
53 | // --- pool options ---
54 | // For all `pool` option parameters:
55 | // @see https://microsoft.github.io/deployr-api-docs/#r-project-pool
56 | pool: {
57 | preloadobjectname: config.constants.TUTORIAL_INSURANCE_MODEL,
58 | preloadobjectauthor: config.constants.TUTORIAL_REPO_OWNER,
59 | preloadobjectdirectory: config.constants.TUTORIAL_REPO_DIRECTORY
60 | }
61 | };
62 |
63 | /*
64 | * 2. Create RBroker instance.
65 | *
66 | * The application designer, in conjunction with the
67 | * DeployR administrator who provisions and sets
68 | * limits on DeployR grid resources, need to decide how
69 | * many R sessions will be reserved for the `PooledRBroker`
70 | * used by our scoring engine application.
71 | */
72 |
73 | console.log('About to create pooledTaskBroker.');
74 |
75 | var pBroker = rbroker.pooledTaskBroker(brokerConfig);
76 |
77 | console.log('R session pool size, requested: ' +
78 | DEMO6_TARGET_POOL_SIZE + ' , actual: ' +
79 | pBroker.maxConcurrency() + '\n');
80 |
81 | /*
82 | * 3. Create an `RTaskAppSimulator`. It will drive
83 | * sample customer data scoring requests through the
84 | * RBroker.
85 | */
86 |
87 | var simulation = new ScoringEngineSimulation(config);
88 |
89 | /*
90 | * 4. Launch RTaskAppSimulator simulation.
91 | */
92 | pBroker.simulateApp(simulation);
93 |
--------------------------------------------------------------------------------
/examples/tutorial/background/background-basics.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /*
4 | * Copyright (C) 2010-2016, Microsoft Corporation
5 | *
6 | * This program is licensed to you under the terms of Version 2.0 of the
7 | * Apache License. This program is distributed WITHOUT
8 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
9 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
10 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
11 | * details.
12 | */
13 |
14 | /*
15 | * usage: $ node background-basics.js
16 | * output: Task execution lifecycle logged to stdout
17 | *
18 | * Example using a BackgroundTaskBroker.
19 | */
20 |
21 | var rbroker = require('../../../rbroker'),
22 | config = require('../../config');
23 |
24 | /*
25 | * 1. Create RBroker instance using RBrokerFactory.
26 | *
27 | * This example creates a BackgroundTaskBroker.
28 | */
29 | var brokerConfig = {
30 | host: config.host,
31 | credentials: config.credentials
32 | };
33 |
34 | /*
35 | * Register a `listener` for asynchronous notifications on RTask completion.
36 | *
37 | * .complete|.error|.progress|.idel are aliases for:
38 | * -------------------------------------------------------
39 | * .on('complete', fn)
40 | * .on('error', fn);
41 | * .on('progress', fn)
42 | * .on('idel'), fn)
43 | */
44 | var bgBroker = rbroker.backgroundTaskBroker(brokerConfig)
45 | .complete(function (rTask, rTaskResult) {
46 | console.log('[completed]-----------------------------------------');
47 | console.log(rTask);
48 | console.log(rTaskResult);
49 | console.log('----------------------------------------------------');
50 | })
51 | .error(function (err) {
52 | console.log('[error]---------------------------------------------');
53 | console.log(err);
54 | console.log('----------------------------------------------------');
55 | })
56 | .progress(function (status) {
57 | console.log('[progress]------------------------------------------');
58 | console.log(status);
59 | console.log('----------------------------------------------------');
60 | })
61 | .idle(function () { // nothing pending
62 | bgBroker.shutdown()
63 | .then(function () {
64 | console.log('Background Task: RBroker shutdown `successful`.');
65 | }, function () {
66 | console.log('Background Task: RBroker has shutdown `failure`.');
67 | });
68 | });
69 |
70 | /*
71 | * 2. Define RTask
72 | *
73 | * This example creates a BackgroundTask that will execute an artibrary block
74 | * of R code.
75 | */
76 | var props = {
77 | name: 'Example Background RTask',
78 | descr: 'Example Background RTask.',
79 | code: 'x <- rnorm(100)'
80 | };
81 |
82 | var rTask = rbroker.backgroundTask(props);
83 |
84 | /*
85 | * 3. Submit RTask to RBroker for execution.
86 | *
87 | * The RTaskToken is returned immediately. You can use the token to track the
88 | * progress of RTask and/or block while waiting for a result. However, in this
89 | * example we are going to allow the RTaskListener handle the result so there is
90 | * nothing further for us to do here after we submit the RTask.
91 | */
92 | bgBroker.submit(rTask);
93 |
94 | console.log('Submitted ' + rTask + ' for execution on RBroker.');
95 |
--------------------------------------------------------------------------------
/lib/rtask-token.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 | var Base = require('selfish').Base;
12 |
13 | /**
14 | * Represents a handle to an `RTask` live on an `RBroker`.
15 | *
16 | * @module rtask-token
17 | * @for rbroker
18 | */
19 | module.exports = Base.extend({
20 |
21 | /**
22 | * Represents a handle to an `RTask` live on an `RBroker`.
23 | *
24 | * @class
25 | * @constructor
26 | * @param {RTask} The task worker.
27 | */
28 | initialize: function initialize(worker) {
29 | this.worker = worker;
30 | this.cancelled = false;
31 | },
32 |
33 | /**
34 | * Terminates `this` running task.
35 | *
36 | * @method cancel
37 | * @param {Boolean} Permission to interrupt task if it is running.
38 | * @return {RTaskToken} for chaining.
39 | * @api public
40 | */
41 | cancel: function(interrupt) {
42 | // RTask completed (resolved|rejected), can not be cancelled.
43 | this.cancelled = this.worker.terminate(interrupt);
44 | return this.cancelled;
45 | },
46 |
47 | /**
48 | * Returns the `RTask` associated with this `RTaskToken`.
49 | *
50 | * @method getTask
51 | * @return {RTaskToken} for chaining.
52 | * @api public
53 | */
54 | getTask: function() {
55 | return this.worker.task;
56 | },
57 |
58 | /**
59 | * Returns `true` if this task completed. Completion may be due to normal
60 | * termination, an exception, or cancellation -- in all of these cases,
61 | * this method will return `true`.
62 | *
63 | * @deprecated
64 | * @method isDone
65 | * @return {Boolean} If `this` task is completed.
66 | * @api public
67 | */
68 | isDone: function() {
69 | return !this.isPending();
70 | },
71 |
72 | /**
73 | * Returns `false` if this task completed. Completion may be due to normal
74 | * termination, an exception, or cancellation -- in all of these cases,
75 | * this method will return `true`.
76 | *
77 | * @method isPending
78 | * @return {Boolean} `true` if this task has not yet been completed.
79 | * @api public
80 | */
81 | isPending: function() {
82 | return this.worker.isPending();
83 | },
84 |
85 | /**
86 | * Returns `true` if this task was cancelled before it completed normally.
87 | *
88 | * @method isCancelled
89 | * @return {Boolean} `true` if this task was cancelled before it completed.
90 | * @api public
91 | */
92 | isCancelled: function() {
93 | return this.cancelled;
94 | },
95 |
96 | /**
97 | * The `.promise()` method returns a dynamically generated Promise that is
98 | * resolved once this task has completed.
99 | *
100 | * @method promise
101 | * @return {Promise} A promise wrapping the resolution of either "resolve"
102 | * or "reject" callback.
103 | * @api public
104 | */
105 | promise: function() {
106 | return this.worker.defer.promise;
107 | }
108 | });
--------------------------------------------------------------------------------
/lib/worker/pooled-task-worker.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var Base = require('selfish').Base,
13 | D = require('d.js'),
14 | merge = require('merge'),
15 | deployr = require('deployr'),
16 | RTaskType = require('../rtask-type'),
17 | RBrokerWorker = require('./rbroker-worker');
18 |
19 | module.exports = RBrokerWorker.extend({
20 |
21 | initialize: function (task, ruser) {
22 | RBrokerWorker.initialize.call(this, task);
23 | this.ruser = ruser;
24 | },
25 |
26 | work: function(resourceToken) {
27 | var task = this.task,
28 | data = merge({
29 | project: resourceToken,
30 | phantom: true
31 | }, this.task.serialize()),
32 | api = '/r/project/execute/' + (data.code ? 'code' : 'script'),
33 | rinputs = data.rinputs || [],
34 | routputs = data.routputs || [],
35 | startTime = new Date().getTime();
36 |
37 | delete data.rinputs;
38 | delete data.routputs;
39 |
40 | this.resourceToken = resourceToken;
41 |
42 | return deployr.io(api)
43 | .share(this.ruser.getCookies())
44 | .data(data)
45 | .rinputs(rinputs)
46 | .routputs(routputs)
47 | .global(false) // supress global error events for this `io`
48 | .promise()
49 | .then(function(res) {
50 | res = res.deployr.response;
51 |
52 | var generatedConsole = res.execution.console,
53 | generatedPlots = res.execution.results,
54 | generatedFiles = res.execution.artifacts,
55 | generatedObjects = res.workspace.objects,
56 | storedFiles = res.repository.files;
57 |
58 | // resolve the promise which kicks-off the callback
59 | return {
60 | task: task,
61 | result: {
62 | id: res.project.project,
63 | type: RTaskType.POOLED,
64 | success: true,
65 | timeOnCode: res.execution.timeCode,
66 | timeOnServer: res.execution.timeTotal,
67 | timeOnCall: (new Date().getTime() - startTime),
68 | failure: null,
69 | interrupted: false,
70 | generatedConsole: generatedConsole,
71 | generatedPlots: generatedPlots,
72 | generatedFiles: generatedFiles,
73 | generatedObjects: generatedObjects,
74 | storedFiles: storedFiles
75 | }
76 | };
77 | })
78 | },
79 |
80 | terminate: function(interrupt) {
81 | // work has started and a forced interrupt given
82 | if (interrupt && this.resourceToken && this.isPending()) {
83 | this.ruser.io('/r/project/execute/interrupt')
84 | .data({ project: this.resourceToken })
85 | .end();
86 | return true;
87 | } else {
88 | //
89 | // RTask still pending confirmation from RBroker if there is no
90 | // `resourceToken` hence can not be interrupted [or] the task is
91 | // being worked on and a forced `interrupt` was not given
92 | //
93 | return false;
94 | }
95 | }
96 |
97 | });
98 |
--------------------------------------------------------------------------------
/lib/worker/discrete-task-worker.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var Base = require('selfish').Base,
13 | deployr = require('deployr'),
14 | D = require('d.js'),
15 | RTaskType = require('../rtask-type'),
16 | RBrokerWorker = require('./rbroker-worker');
17 |
18 | module.exports = RBrokerWorker.extend({
19 |
20 | initialize: function(task, ruser) {
21 | RBrokerWorker.initialize.call(this, task);
22 | this.ruser = ruser;
23 | this.io = null; // `io` request for 'termination' if called
24 | },
25 |
26 | work: function(resourceToken) {
27 | var task = this.task,
28 | startTime = new Date().getTime(),
29 | data = this.task.serialize(),
30 | rinputs = data.rinputs || [],
31 | routputs = data.routputs || [];
32 |
33 | delete data.rinputs;
34 | delete data.routputs;
35 |
36 | this.resourceToken = resourceToken;
37 |
38 | // save `io` request for 'termination' purposes
39 | this.io = deployr.io('/r/repository/script/execute')
40 | // make parallel http req. using the same session
41 | .share(this.ruser ? this.ruser.getCookies() : null)
42 | .data(data)
43 | .rinputs(rinputs)
44 | .routputs(routputs)
45 | .global(false); // supress global error events for this `io`
46 |
47 | // send the script execution request and return a promise
48 | return this.io.promise()
49 | .then(function(res) {
50 | res = res.deployr.response;
51 |
52 | var timeOnServer = res.execution.timeTotal,
53 | id = res.project.project,
54 | generatedConsole = res.execution.console,
55 | generatedPlots = res.execution.results,
56 | generatedFiles = res.execution.artifacts,
57 | generatedObjects = res.workspace.objects,
58 | storedFiles = res.repository.files;
59 |
60 | // resolve the promise which kicks-off the callback values
61 | return {
62 | task: task,
63 | result: {
64 | id: id,
65 | type: RTaskType.DISCRETE,
66 | success: true,
67 | timeOnCode: res.execution.timeCode,
68 | timeOnServer: timeOnServer,
69 | timeOnCall: (new Date().getTime() - startTime),
70 | failure: null,
71 | interrupted: false,
72 | generatedConsole: generatedConsole,
73 | generatedPlots: generatedPlots,
74 | generatedFiles: generatedFiles,
75 | generatedObjects: generatedObjects,
76 | storedFiles: storedFiles
77 | }
78 | };
79 | });
80 | },
81 |
82 | terminate: function(interrupt) {
83 | //
84 | // @NOTE:
85 | // There is no way to obtain DeployR reference, such as a projectId, for
86 | // an stateless execution in-progress, so aborting the current RTask
87 | // operation is not possible. At best we can do here is free-up the
88 | // client connection and abort.
89 | //
90 | if (interrupt && this.io && this.isPending()) {
91 | this.io.abort();
92 | this.io.destroy();
93 | this.io = null;
94 |
95 | return true;
96 | } else {
97 | //
98 | // RTask still pending confirmation from RBroker if there is no
99 | // `resourceToken` hence can not be interrupted [or] the task is
100 | // being worked on and a forced `interrupt` was not given
101 | //
102 | return false;
103 | }
104 | }
105 | });
106 |
--------------------------------------------------------------------------------
/examples/tutorial/pooled/pooled-simulation.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Example using a Pooled Simulation
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | Example using a Pooled Simulation
16 |
17 |
18 | - Populate /examples/config.json with the proper values before running.
19 | - Open the browser's debug window to view print logs for this example.
20 | - View documentation for more information.
21 |
22 |
23 |
115 |
116 |
--------------------------------------------------------------------------------
/examples/tutorial/discrete/discrete-basics.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /*
4 | * Copyright (C) 2010-2016, Microsoft Corporation
5 | *
6 | * This program is licensed to you under the terms of Version 2.0 of the
7 | * Apache License. This program is distributed WITHOUT
8 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
9 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
10 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
11 | * details.
12 | */
13 |
14 | /*!
15 | * usage: $ node discrete-basics.js
16 | * output: Task execution lifecycle logged to stdout
17 | *
18 | * Example using a DiscreteTaskBroker.
19 | */
20 |
21 | var rbroker = require('../../../rbroker'),
22 | config = require('../../config'),
23 | consts = config.constants;
24 |
25 | /*
26 | * 1. Create RBroker instance using RBrokerFactory.
27 | *
28 | * This example creates a DiscreteTaskBroker.
29 | */
30 | var brokerConfig = {
31 | maxConcurrentTaskLimit: 10,
32 | host: config.host,
33 | credentials: config.credentials
34 | };
35 |
36 | //
37 | // .complete|.error|.progress|.idel are aliases for:
38 | // -------------------------------------------------------
39 | // .on('complete', fn)
40 | // .on('error', fn);
41 | // .on('progress', fn)
42 | // .on('idel'), fn)
43 | //
44 | var dBroker = rbroker.discreteTaskBroker(brokerConfig)
45 | .complete(function (rTask, rTaskResult) {
46 | console.log('[completed]----------------------------------------');
47 | console.log(rTask);
48 | console.log(rTaskResult);
49 | console.log('---------------------------------------------------');
50 | })
51 | .error(function (err) {
52 | console.log('[error]--------------------------------------------');
53 | console.log(err);
54 | console.log('---------------------------------------------------');
55 | })
56 | .progress(function (status) {
57 | console.log('[progress]-----------------------------------------');
58 | console.log(status);
59 | console.log('---------------------------------------------------');
60 | })
61 | .idle(function () { // nothing pending
62 | dBroker.shutdown()
63 | .then(function () {
64 | console.log('Discrete: RBroker shutdown `successful`.');
65 | }, function () {
66 | console.log('Discrete: RBroker has shutdown `failure`.');
67 | });
68 | });
69 |
70 | /*
71 | * 2. Define RTask(2)
72 | *
73 | * This example creates 7 DiscreteTasks that will execute an R script:
74 | * /testuser/root/DeployR - Hello World.R
75 | */
76 | var props = {
77 | filename: consts.TUTORIAL_NOOP_SCRIPT,
78 | directory: consts.TUTORIAL_REPO_DIRECTORY,
79 | author: consts.TUTORIAL_REPO_OWNER
80 | };
81 |
82 | var rTask1 = rbroker.discreteTask(props);
83 | var rTask2 = rbroker.discreteTask(props);
84 | var rTask3 = rbroker.discreteTask(props);
85 | var rTask4 = rbroker.discreteTask(props);
86 | var rTask5 = rbroker.discreteTask(props);
87 | var rTask6 = rbroker.discreteTask(props);
88 | var rTask7 = rbroker.discreteTask(props);
89 |
90 | /*
91 | *
92 | * 3. Submit RTasks to RBroker for execution.
93 | *
94 | * The RTaskToken is returned immediately. You can use the returned token to
95 | * bind to it's task lifecycle (complete|error|ensure), an example of this is
96 | * the rTask7 task.
97 | */
98 | dBroker.submit(rTask1, false);
99 | dBroker.submit(rTask2, false);
100 | dBroker.submit(rTask3, false);
101 | dBroker.submit(rTask4, false);
102 | dBroker.submit(rTask5, false);
103 | dBroker.submit(rTask6, false);
104 | // -- Listen for indifidual task --
105 | dBroker.submit(rTask7, false)
106 | .promise()
107 | .then(function (res) {
108 | var rTask = res.task,
109 | rTaskResult = res.result;
110 |
111 | console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~ Individual TASK completed');
112 | console.log(rTask);
113 | console.log(rTaskResult);
114 | console.log('----------------------------------------------------');
115 | })
116 | .error(function (err) {
117 | console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Individual TASK error');
118 | console.log(err);
119 | console.log('----------------------------------------------------');
120 | })
121 | .ensure(function () {
122 | console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Individual TASK ensure');
123 | console.log(rTask7);
124 | console.log('----------------------------------------------------');
125 | });
126 |
127 | // Print
128 | console.log('Discrete Task: submitted ' + rTask1 + ' for execution on RBroker');
129 | console.log('Discrete Task: submitted ' + rTask2 + ' for execution on RBroker');
130 | console.log('Discrete Task: submitted ' + rTask3 + ' for execution on RBroker');
131 | console.log('Discrete Task: submitted ' + rTask4 + ' for execution on RBroker');
132 | console.log('Discrete Task: submitted ' + rTask5 + ' for execution on RBroker');
133 | console.log('Discrete Task: submitted ' + rTask6 + ' for execution on RBroker');
134 | console.log('Discrete Task: submitted ' + rTask7 + ' for execution on RBroker');
--------------------------------------------------------------------------------
/examples/utils/rbroker-stats-helper.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 | (function(global) {
12 |
13 | var taskType = {
14 | DISCRETE: 'DISCRETE',
15 | POOLED: 'POOLED',
16 | BACKGROUND: 'BACKGROUND'
17 | };
18 |
19 | /*
20 | * RBroker runtime summary statistics (print) helper class.
21 | */
22 | var printHelper = {
23 |
24 | /**
25 | * Prints RBroker Runtime Stats to console output.
26 | */
27 | stats: function(stats) {
28 | var displayAvgTimeOnCode = 0,
29 | displaySAvgTimeOnServer = 0,
30 | displayAvgTimeOnCall = 0,
31 | maxConcurrency = stats.maxConcurrency;
32 |
33 | console.log('[progress]-----------------------------------------');
34 | console.log('RBroker Activity Summary');
35 | console.log('RBroker: Max Concurrency [ ' + maxConcurrency + ' ]');
36 | console.log('RBroker: Total Tasks Run [ ' +
37 | stats.totalTasksRun + ' ]');
38 | console.log('RBroker: Tasks Ok [ ' +
39 | stats.totalTasksRunToSuccess + ' ] Fail [ ' +
40 | stats.totalTasksRunToFailure + ' ]');
41 |
42 | if (stats.totalTasksRunToSuccess > 0) {
43 | displayAvgTimeOnCode =
44 | stats.totalTimeTasksOnCode / stats.totalTasksRunToSuccess;
45 | displayAvgTimeOnServer =
46 | stats.totalTimeTasksOnServer / stats.totalTasksRunToSuccess;
47 | displayAvgTimeOnCall =
48 | stats.totalTimeTasksOnCall / stats.totalTasksRunToSuccess;
49 | }
50 |
51 | console.log('RBroker: Task Average Time On Code [ ' +
52 | displayAvgTimeOnCode + ' ]');
53 | console.log('RBroker: Task Average Time On Server [ ' +
54 | displayAvgTimeOnServer + ' ]');
55 | console.log('RBroker: Task Average Time On Call [ ' +
56 | displayAvgTimeOnCall + ' ]\n');
57 |
58 | console.log('---------------------------------------------------');
59 | },
60 |
61 | /**
62 | * Prints errors to console output.
63 | */
64 | error: function(err) {
65 | console.log('[error]--------------------------------------------');
66 | console.log('Status[fail]: cause= ' + err);
67 | console.log('---------------------------------------------------');
68 | },
69 |
70 | /**
71 | * Prints RTaskResult to console output.
72 | */
73 | results: function(task, result) {
74 | console.log('[completed]----------------------------------------');
75 | console.log('Task: ' + task);
76 |
77 | switch (result.type) {
78 |
79 | case taskType.DISCRETE:
80 | if (result.success) {
81 | console.log('Status[ok]: [ code : ' +
82 | result.timeOnCode + ' , server : ' +
83 | result.timeOnServer + ' , call : ' +
84 | result.timeOnCall + ' ]');
85 | } else {
86 | console.log('Status[fail]: cause=' + result.failure);
87 | }
88 | break;
89 |
90 | case taskType.POOLED:
91 | if (result.success) {
92 | console.log('Status[ok]: [ code : ' +
93 | result.timeOnCode + ' , server : ' +
94 | result.timeOnServer + ' , call : ' +
95 | result.timeOnCall + ' ]');
96 | } else {
97 | console.log('Status[fail]: cause=' + result.failure);
98 | }
99 | break;
100 |
101 | case taskType.BACKGROUND:
102 | if (result.success) {
103 | console.log('Status[ok]: [ server : ' +
104 | result.timeOnServer + ' , call : ' +
105 | result.timeOnCall + ' ]');
106 | } else {
107 | console.log('Status[fail]: cause=' + result.failure);
108 | }
109 | break;
110 | }
111 | console.log('---------------------------------------------------');
112 | }
113 | };
114 |
115 | // -- export for both Browser|Node.js so we can reuse for both envs. --
116 | if (typeof exports !== 'undefined') {
117 | if (typeof module !== 'undefined' && module.exports) {
118 | exports = module.exports = printHelper;
119 | }
120 | exports.printHelper = printHelper;
121 | } else {
122 | global.printHelper = printHelper;
123 | }
124 |
125 | })(typeof window === 'undefined' ? this : window);
126 |
--------------------------------------------------------------------------------
/examples/tutorial/simulations/browser/scoring-engine-simulation.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | /*
13 | * This ScoringEngineSimulation is a simple extension
14 | * to the SampleAppSimulation class used by PooledSimulation.
15 | *
16 | * The addition of the SIMULATE_TASK_RATE_PER_MINUTE parameter
17 | * allows the app simulation to mimick more "real world"
18 | * workloads by adjusting the rate at which RTask are
19 | * submitted to the RBroker for execution.
20 | *
21 | * Note, this simulation also demonstrates how input values
22 | * can be passed to each RTask and how output values can
23 | * be retrieved following the execution of each task.
24 | *
25 | * In this example, a "customerid" input value is passed on
26 | * each RTask. While this example passes a simple index
27 | * value for "customerid" a "real world" app would like pass
28 | * a database record id along with additional customer specific
29 | * data parameters.
30 | *
31 | * In this example, the value of the "score" is returned as a
32 | * DeployR-encoded R object and made available on the result
33 | * for the RTask.
34 | */
35 | (function(global) {
36 |
37 | function ScoringEngineSimulation(config) {
38 | this.consts = config.constants;
39 | }
40 |
41 | ScoringEngineSimulation.prototype = {
42 |
43 | /**
44 | * @IMPORTANT - The `simulateApp(broker)` method must be implemented.
45 | */
46 | simulateApp: function(pBroker) {
47 | var SIMULATE_TOTAL_TASK_COUNT = 10,
48 | SIMULATE_TASK_RATE_PER_MINUTE = 0,
49 | simulationStartTime = 0,
50 | sleep = 0,
51 | consts = this.consts,
52 | RIn = deployr.RInput;
53 |
54 | /*
55 | * The following logic controls the simulated rate of
56 | * RTasks being submitted to the RBroker, essentially
57 | * controlling the simulated workload.
58 | */
59 | function staggeredLoad(tasksPushedToBroker, sleep) {
60 | /*
61 | * Prepare RTask for real-time scoring.
62 | *
63 | * In this example, we pass along a unique customer ID
64 | * with each RTask. In a real-world application, the input
65 | * parameters on each RTask will vary depending on need,
66 | * such as customer database record keys and supplementary
67 | * parameter data to facilitate the scoring.
68 | */
69 | setTimeout(function() {
70 |
71 | var rTask = rbroker.pooledTask({
72 | filename: consts.TUTORIAL_RTSCORE_SCRIPT,
73 | directory: consts.TUTORIAL_REPO_DIRECTORY,
74 | author: consts.TUTORIAL_REPO_OWNER,
75 | rinputs: [RIn.numeric('customerid', tasksPushedToBroker)],
76 | routputs: ['score']
77 | });
78 |
79 | pBroker.submit(rTask, false);
80 | console.log('Submitted task ' + rTask + '\n');
81 | }, sleep);
82 | }
83 |
84 | /*
85 | * Listen for the RTask events.
86 | *
87 | * .complete|.error|.progress|.idel are aliases for:
88 | * -------------------------------------------------------
89 | * .on('complete', fn)
90 | * .on('error', fn);
91 | * .on('progress', fn)
92 | * .on('idel'), fn)
93 | */
94 | pBroker.complete(function(rTask, rTaskResult) {
95 | printHelper.results(rTask, rTaskResult);
96 | })
97 | .error(function(err) {
98 | printHelper.error(err);
99 | })
100 | .progress(function(status) {
101 | printHelper.stats(status);
102 | })
103 | .idle(function() {
104 | pBroker.shutdown()
105 | .then(function() {
106 | console.log('Pooled: RBroker shutdown `successful`.');
107 | }, function() {
108 | console.log('Pooled: RBroker has shutdown `failure`.');
109 | });
110 | });
111 |
112 | /*
113 | * Submit task(s) to RBroker for execution.
114 | */
115 |
116 | console.log('About to simulate ' +
117 | SIMULATE_TOTAL_TASK_COUNT + ' tasks at a rate of ' +
118 | SIMULATE_TASK_RATE_PER_MINUTE + ' tasks per minutes.');
119 |
120 | simulationStartTime = new Date().getTime();
121 |
122 | for (var tasksPushedToBroker = 0;
123 | tasksPushedToBroker < SIMULATE_TOTAL_TASK_COUNT;
124 | tasksPushedToBroker++) {
125 |
126 | /*
127 | * The following logic controls the simulated rate of
128 | * RTasks being submitted to the RBroker, essentially
129 | * controlling the simulated workload.
130 | */
131 | if (tasksPushedToBroker < (SIMULATE_TOTAL_TASK_COUNT)) {
132 | if (SIMULATE_TASK_RATE_PER_MINUTE !== 0) {
133 | var staggerLoadInterval = 60 / SIMULATE_TASK_RATE_PER_MINUTE;
134 | sleep += (staggerLoadInterval * 1000);
135 | }
136 | staggeredLoad(tasksPushedToBroker, sleep);
137 | }
138 | }
139 | }
140 | };
141 |
142 | global.ScoringEngineSimulation = ScoringEngineSimulation;
143 |
144 | })(window);
--------------------------------------------------------------------------------
/examples/tutorial/simulations/nodejs/scoring-engine-simulation.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /*
4 | * Copyright (C) 2010-2016, Microsoft Corporation
5 | *
6 | * This program is licensed to you under the terms of Version 2.0 of the
7 | * Apache License. This program is distributed WITHOUT
8 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
9 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
10 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
11 | * details.
12 | */
13 |
14 | var deployr = require('deployr'),
15 | rbroker = require('../../../../rbroker'),
16 | printHelper = require('../../../utils/rbroker-stats-helper');
17 |
18 | /*
19 | * This ScoringEngineSimulation is a simple extension
20 | * to the SampleAppSimulation class used by PooledSimulation.
21 | *
22 | * The addition of the SIMULATE_TASK_RATE_PER_MINUTE parameter
23 | * allows the app simulation to mimick more "real world"
24 | * workloads by adjusting the rate at which RTask are
25 | * submitted to the RBroker for execution.
26 | *
27 | * Note, this simulation also demonstrates how input values
28 | * can be passed to each RTask and how output values can
29 | * be retrieved following the execution of each task.
30 | *
31 | * In this example, a "customerid" input value is passed on
32 | * each RTask. While this example passes a simple index
33 | * value for "customerid" a "real world" app would like pass
34 | * a database record id along with additional customer specific
35 | * data parameters.
36 | *
37 | * In this example, the value of the "score" is returned as a
38 | * DeployR-encoded R object and made available on the result
39 | * for the RTask.
40 | */
41 | function ScoringEngineSimulation(config) {
42 | this.consts = config.constants;
43 | }
44 |
45 | ScoringEngineSimulation.prototype = {
46 |
47 | /**
48 | * @IMPORTANT - The `simulateApp(broker)` method must be implemented.
49 | */
50 | simulateApp: function(pBroker) {
51 | var SIMULATE_TOTAL_TASK_COUNT = 10,
52 | SIMULATE_TASK_RATE_PER_MINUTE = 0,
53 | simulationStartTime = 0,
54 | sleep = 0,
55 | consts = this.consts,
56 | RIn;
57 |
58 | if (typeof window === 'undefined') {
59 | RIn = require('deployr').RInput;
60 | } else {
61 | RIn = deployr.RInput; // `deployr` already in window for browsers
62 | }
63 |
64 | /*
65 | * The following logic controls the simulated rate of
66 | * RTasks being submitted to the RBroker, essentially
67 | * controlling the simulated workload.
68 | */
69 | function staggeredLoad(tasksPushedToBroker, sleep) {
70 | /*
71 | * Prepare RTask for real-time scoring.
72 | *
73 | * In this example, we pass along a unique customer ID
74 | * with each RTask. In a real-world application, the input
75 | * parameters on each RTask will vary depending on need,
76 | * such as customer database record keys and supplementary
77 | * parameter data to facilitate the scoring.
78 | */
79 | setTimeout(function() {
80 |
81 | var rTask = rbroker.pooledTask({
82 | filename: consts.TUTORIAL_RTSCORE_SCRIPT,
83 | directory: consts.TUTORIAL_REPO_DIRECTORY,
84 | author: consts.TUTORIAL_REPO_OWNER,
85 | rinputs: [RIn.numeric('customerid', tasksPushedToBroker)],
86 | routputs: ['score']
87 | });
88 |
89 | pBroker.submit(rTask, false);
90 | console.log('Submitted task ' + rTask + '\n');
91 | }, sleep);
92 | }
93 |
94 | /*
95 | * Listen for the RTask events.
96 | *
97 | * .complete|.error|.progress|.idel are aliases for:
98 | * -------------------------------------------------------
99 | * .on('complete', fn)
100 | * .on('error', fn);
101 | * .on('progress', fn)
102 | * .on('idel'), fn)
103 | */
104 | pBroker.complete(function(rTask, rTaskResult) {
105 | printHelper.results(rTask, rTaskResult);
106 | })
107 | .error(function(err) {
108 | printHelper.error(err);
109 | })
110 | .progress(function(status) {
111 | printHelper.stats(status);
112 | })
113 | .idle(function() {
114 | pBroker.shutdown()
115 | .then(function() {
116 | console.log('Pooled: RBroker shutdown `successful`.');
117 | }, function() {
118 | console.log('Pooled: RBroker has shutdown `failure`.');
119 | });
120 | });
121 |
122 | /*
123 | * Submit task(s) to RBroker for execution.
124 | */
125 |
126 | console.log('About to simulate ' +
127 | SIMULATE_TOTAL_TASK_COUNT + ' tasks at a rate of ' +
128 | SIMULATE_TASK_RATE_PER_MINUTE + ' tasks per minutes.');
129 |
130 | simulationStartTime = new Date().getTime();
131 |
132 | for (var tasksPushedToBroker = 0; tasksPushedToBroker < SIMULATE_TOTAL_TASK_COUNT; tasksPushedToBroker++) {
133 |
134 | /*
135 | * The following logic controls the simulated rate of
136 | * RTasks being submitted to the RBroker, essentially
137 | * controlling the simulated workload.
138 | */
139 | if (tasksPushedToBroker < (SIMULATE_TOTAL_TASK_COUNT)) {
140 | if (SIMULATE_TASK_RATE_PER_MINUTE !== 0) {
141 | var staggerLoadInterval = 60 / SIMULATE_TASK_RATE_PER_MINUTE;
142 | sleep += (staggerLoadInterval * 1000);
143 | }
144 | staggeredLoad(tasksPushedToBroker, sleep);
145 | }
146 | }
147 | }
148 | };
149 |
150 | module.exports = ScoringEngineSimulation;
--------------------------------------------------------------------------------
/examples/tutorial/pooled/pooled-basics.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /*
4 | * Copyright (C) 2010-2016, Microsoft Corporation
5 | *
6 | * This program is licensed to you under the terms of Version 2.0 of the
7 | * Apache License. This program is distributed WITHOUT
8 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
9 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
10 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
11 | * details.
12 | */
13 |
14 | /*!
15 | * usage: $ node pooled-basics.js
16 | * output: Task execution lifecycle logged to stdout
17 | *
18 | * Example using a PooledTaskBroker.
19 | */
20 | var rbroker = require('../../../rbroker'),
21 | config = require('../../config'),
22 | consts = config.constants;
23 |
24 | /*
25 | * 1. Create RBroker instance using RBrokerFactory.
26 | *
27 | * This example creates a PooledTaskBroker.
28 | */
29 | var brokerConfig = {
30 | maxConcurrentTaskLimit: 10,
31 | host: config.host,
32 | credentials: config.credentials,
33 | releaseGridResources: false,
34 | logging: false,
35 | // --- pool options ---
36 | // For all `pool` option parameters:
37 | // @see https://microsoft.github.io/deployr-api-docs/#r-project-pool
38 | pool: { /* optional pooled parameters go here... */ }
39 | };
40 |
41 | /*
42 | * .complete|.error|.progress|.idel are aliases for:
43 | * -------------------------------------------------------
44 | * .on('complete', fn)
45 | * .on('error', fn);
46 | * .on('progress', fn)
47 | * .on('idel'), fn)
48 | */
49 | var pBroker = rbroker.pooledTaskBroker(brokerConfig)
50 | .complete(function (rTask, rTaskResult) {
51 | console.log('[completed]----------------------------------------');
52 | console.log(rTask);
53 | console.log(rTaskResult);
54 | console.log('---------------------------------------------------');
55 | })
56 | .error(function (err) {
57 | console.log('[error]--------------------------------------------');
58 | console.log(err);
59 | console.log('---------------------------------------------------');
60 | })
61 | .progress(function (status) {
62 | console.log('[progress]-----------------------------------------');
63 | console.log(status);
64 | console.log('---------------------------------------------------');
65 | })
66 | .idle(function () { // nothing pending
67 |
68 | var shutdown = {
69 | start: function () {
70 | console.log('Pooled Task: rBroker shutdown `started`.');
71 | return pBroker.shutdown();
72 | },
73 | success: function () {
74 | console.log('Pooled Task: rBroker shutdown `successful`.');
75 | },
76 | failure: function () {
77 | console.log('Pooled Task: rBroker shutdown `failure`.');
78 | }
79 | };
80 |
81 | // -- refresh then shutdown then print shutdown status --
82 | //
83 | // @NOTE - A `refresh` is not needed here it simply demonstrates a
84 | // the proper state to call `refresh` which is when we are idel
85 | pBroker.refresh()
86 | .then(shutdown.start)
87 | .then(shutdown.success, shutdown.failure);
88 |
89 | });
90 |
91 | /*
92 | * 2. Define RTask
93 | *
94 | * This example creates 7 PooledTasks that will execute the same R script,
95 | * `testuser/root/DeployR - Hello World.R`
96 | */
97 | var props = {
98 | // You can use `code` | `filename` for a script
99 | filename: consts.TUTORIAL_NOOP_SCRIPT,
100 | //code: 'x<-5 \n print(x)',
101 | directory: consts.TUTORIAL_REPO_DIRECTORY,
102 | author: consts.TUTORIAL_REPO_OWNER
103 | };
104 |
105 | var rTask1 = rbroker.pooledTask(props);
106 | var rTask2 = rbroker.pooledTask(props);
107 | var rTask3 = rbroker.pooledTask(props);
108 | var rTask4 = rbroker.pooledTask(props);
109 | var rTask5 = rbroker.pooledTask(props);
110 | var rTask6 = rbroker.pooledTask(props);
111 | var rTask7 = rbroker.pooledTask(props);
112 |
113 | /*
114 | *
115 | * 3. Submit RTasks to RBroker for execution.
116 | *
117 | * The `Task Worker` is returned immediately. You can use the returned token to
118 | * bind to it's task lifecycle (complete|error|ensure), an example of this is
119 | * the rTask7 task submittal.
120 | */
121 | pBroker.submit(rTask1, false);
122 | pBroker.submit(rTask2, false);
123 | pBroker.submit(rTask3, false);
124 | pBroker.submit(rTask4, false);
125 | pBroker.submit(rTask5, false);
126 | pBroker.submit(rTask6, false);
127 | pBroker.submit(rTask7, false)
128 | .promise()
129 | .then(function (res) {
130 | var rTask = res.task,
131 | rTaskResult = res.result;
132 |
133 | console.log('~~~~~~~~~~~~~~~~~~~~~~~~~ Individual TASK completed');
134 | console.log(rTask);
135 | console.log(rTaskResult);
136 | console.log('---------------------------------------------------');
137 | })
138 | .error(function (err) {
139 | console.log(err);
140 | })
141 | .ensure(function () {
142 | console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Individual TASK ensure');
143 | console.log(rTask7);
144 | console.log('----------------------------------------------------');
145 | });
146 |
147 | // Print
148 | console.log('Pooled Task: submitted ' + rTask1 + ' for execution on RBroker.');
149 | console.log('Pooled Task: submitted ' + rTask2 + ' for execution on RBroker.');
150 | console.log('Pooled Task: submitted ' + rTask3 + ' for execution on RBroker.');
151 | console.log('Pooled Task: submitted ' + rTask4 + ' for execution on RBroker.');
152 | console.log('Pooled Task: submitted ' + rTask5 + ' for execution on RBroker.');
153 | console.log('Pooled Task: submitted ' + rTask6 + ' for execution on RBroker.');
154 | console.log('Pooled Task: submitted ' + rTask7 + ' for execution on RBroker.');
155 |
--------------------------------------------------------------------------------
/test/fixtures/job.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var deployr = require('deployr'),
13 | when = require('when'),
14 | merge = require('merge'),
15 | config = require('../config');
16 |
17 | var JOB_DONE_STATES = {
18 | COMPLETED: 'Completed',
19 | CANCELLED: 'Cancelled',
20 | INTERRUPTED: 'Interrupted',
21 | FAILED: 'Failed',
22 | ABORTED: 'Aborted'
23 | };
24 |
25 | function queryJob(ruser, jobID) {
26 | return deployr.io('/r/job/query')
27 | .data({
28 | job: jobID
29 | })
30 | .share(ruser.getCookies())
31 | .promise();
32 | };
33 |
34 | module.exports = merge({
35 |
36 | /*
37 | * Following a unit test all artifacts assocated with an
38 | * RJob should be removed from the DeployR database. This
39 | * implementation removes the RJob and the associated
40 | * RProject if one was generated as a result of the RJob.
41 | *
42 | * Note, this method will not throw any Exceptions.
43 | */
44 | deleteJobArtifacts: function(ruser, jobID) {
45 | //
46 | // This runs in parrell to the below
47 | // 1. /r/job/query
48 | // 2. /r/project/delete
49 | //
50 | deployr.io('/r/job/query')
51 | .data({
52 | job: jobID
53 | })
54 | .share(ruser.getCookies())
55 | .end(function(res) {
56 | return {
57 | project: res.get('project')
58 | }
59 | })
60 | .io('/r/project/delete')
61 | .end();
62 |
63 | //
64 | // This runs in parrell to the above
65 | // 1. /r/job/cancel
66 | // 2. /r/job/delete
67 | // They are slipt up to ensure `/r/job/cancel` and `/r/job/delete`
68 | // get invoked
69 | //
70 | return deployr.io('/r/job/cancel')
71 | .data({
72 | job: jobID
73 | })
74 | .share(ruser.getCookies())
75 | .end()
76 | .io('/r/job/delete')
77 | .data({
78 | job: jobID
79 | })
80 | .promise();
81 | },
82 |
83 | /*
84 | * Test and verify that a Job has reached a given "status".
85 | * This implementation will busy-wait for up to 2 minutes
86 | * for the Job to complete, otherwise it returns failure.
87 | */
88 | verifyJobExitStatus: function(ruser, jobID, status) {
89 | var statusCheck = {
90 | id: null,
91 | PING_INTERVAL: 1000,
92 | TWO_MINUTES: 120000,
93 |
94 | start: function() {
95 | var self = this,
96 | time = 0,
97 | TWO_MINUTES = this.TWO_MINUTES,
98 | PING_INTERVAL = this.PING_INTERVAL,
99 | stop = function(res) {
100 | self.stop(res);
101 | };
102 |
103 | clearInterval(this.id);
104 |
105 | this.id = setInterval(function() {
106 | time = time + PING_INTERVAL;
107 |
108 | if (time >= TWO_MINUTES) {
109 | stop();
110 | }
111 |
112 | queryJob(ruser, jobID).then(stop, stop);
113 |
114 | }, PING_INTERVAL);
115 |
116 | queryJob(ruser, jobID).then(stop, stop);
117 |
118 | this.defer = when.defer();
119 |
120 | return this.defer.promise;
121 | },
122 |
123 | stop: function(res) {
124 | if (res) {
125 | if (!res.get('success')) {
126 | clearInterval(this.id);
127 | this.defer.resolve({
128 | status: false,
129 | cause: res.get('error')
130 | });
131 | } else {
132 | if (res.get('status') === status) {
133 | //
134 | // status matches status to be
135 | // verified, break.
136 | //
137 | clearInterval(this.id);
138 | this.defer.resolve({
139 | status: true,
140 | cause: status
141 | });
142 | } else if (JOB_DONE_STATES[res.get('status').toUpperCase()]) {
143 | //
144 | // JobDetails.status matches a "done"
145 | // state that is not the state to be
146 | // verified, so break.
147 | //
148 | clearInterval(this.id);
149 | this.defer.resolve({
150 | status: false,
151 | cause: 'status matches a "done" state ' +
152 | 'that is not the state to be verified.'
153 | });
154 | //this.defer.reject('status matches a "done" state ' +
155 | // 'that is not the state to be verified.');
156 | } else { /* continue job status checks */ }
157 | }
158 | } else {
159 | clearInterval(this.id);
160 | this.defer.resolve({
161 | status: false,
162 | cause: 'timeout after 2 minutes.'
163 | });
164 | }
165 | }
166 | };
167 |
168 | return statusCheck.start();
169 | }
170 | }, require('./common'));
171 |
--------------------------------------------------------------------------------
/examples/tutorial/background/background-basics.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Example using a Background Task Broker
8 |
9 |
10 |
11 |
12 |
13 | Example using a Background Task Broker
14 |
15 |
16 | - Populate /examples/config.json with the proper values before running.
17 | - Open the browser's debug window to view print logs for this example.
18 | - View documentation for more information.
19 |
20 |
21 |
151 |
152 |
--------------------------------------------------------------------------------
/lib/engine/pooled-task-broker.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var deployr = require('deployr');
13 |
14 | var D = require('d.js'),
15 | merge = require('merge'),
16 | RBrokerEngine = require('./rbroker-engine'),
17 | PooledTaskWorker = require('../worker/pooled-task-worker');
18 |
19 | /**
20 | * The Pooled Task Runtime acquires a dedicated pool of DeployR grid resources
21 | * at startup.
22 | *
23 | * @module pooled-task-broker
24 | * @for rbroker
25 | */
26 | module.exports = RBrokerEngine.extend({
27 | /**
28 | * The Pooled Task Runtime acquires a dedicated pool of DeployR grid resources
29 | * at startup.
30 | *
31 | * @class
32 | * @constructor
33 | * @param {Object} [options] Configuration options.
34 | */
35 | initialize: function (config) {
36 | RBrokerEngine.initialize.call(this, config);
37 |
38 | if (!config.credentials) {
39 | throw new Error('Broker failed to initialize, user credentials required.');
40 | }
41 |
42 | var self = this,
43 | poolSize = this.parallelTaskLimit,
44 | pool = merge({ poolsize: poolSize }, config.pool || {});
45 |
46 | /*
47 | * Prevents authenticated HTTP session from timing out due to inactivity to
48 | * ensure pool of RProject remain live and available to PooledTaskBroker.
49 | */
50 | this.httpKeepAlive = {
51 | id: null,
52 | PING_INTERVAL: 60000,
53 |
54 | start: function(ruser) {
55 | this.stop();
56 | this.id = setInterval(function() {
57 | deployr.io('/r/user/about').share(ruser.getCookies()).end();
58 | }, this.PING_INTERVAL);
59 | },
60 |
61 | stop: function() {
62 | clearInterval(this.id);
63 | }
64 | };
65 |
66 | /*
67 | * Initialize the resourceTokenPool with RProject.
68 | */
69 | function load(res) {
70 | var projects = res.get('projects');
71 |
72 | if (projects.length > 0) {
73 | // inform caller of any `Grid Notification` errors usch as:
74 | // 'Concurrent authenticated project limit (10) reached for user XYZ.'
75 | // These 'errors' are more like warnings than runtime throwable errors.
76 | var warn = res.get('error');
77 | if (warn) { self.emit('warning', warn); }
78 |
79 | projects.forEach(function(project) {
80 | self.resourceTokenPool.offer(project.project);
81 | });
82 |
83 | self.parallelTaskLimit = self.resourceTokenPool.size();
84 | self.httpKeepAlive.start(self.ruser);
85 | self.emit('ready');
86 | } else { // No projects were created (projects.length == 0)
87 | self.emit('error', res.get('error'));
88 | }
89 | }
90 |
91 | //
92 | // Validate DeployR server `endpoint` and authenticate.
93 | //
94 | this.ruser = this.validateEndpoint().io('/r/user/login')
95 | .data(config.credentials)
96 | .ctx(this)
97 | .end(function() {
98 | //
99 | // Build the project pool.
100 | //
101 | if (config.releaseGridResources) {
102 | this.ruser.io('/r/user/release')
103 | .end()
104 | .io('/r/project/pool')
105 | .data(pool)
106 | .end(load);
107 | } else {
108 | this.ruser.io('/r/project/pool').data(pool).end(load);
109 | }
110 | });
111 | },
112 |
113 | /**
114 | * Refresh the configuration for `PooledTaskRBroker`.
115 | *
116 | * A refresh causes all workspace objects and directory files in the
117 | * underlying R sessions within the pool to be cleared before new workspace
118 | * objects and/or directory files are loaded per the new config options.
119 | *
120 | * Only an idle RBroker instance can be refreshed.
121 | *
122 | * @method refresh
123 | * @param {Object} Startup options for a `PooledTaskRBroker`.
124 | * @override
125 | * @return {Promise} A promise wrapping the resolution of either "resolve" or
126 | * "reject" callback.
127 | * @api public
128 | */
129 | refresh: function (config) {
130 | var calls = [],
131 | data = {},
132 | ruser = this.ruser;
133 |
134 | if(!this.isIdle()) {
135 | //var defer = when.defer();
136 | //defer.reject(new Error('RBroker is not idle, refresh not permitted.'));
137 | //return defer.promise;
138 | return D.rejected(new Error('RBroker is not idle, refresh not permitted.'));
139 | }
140 |
141 | // assert only the proper parameters are on the call
142 | config = config || {};
143 | data = {
144 | code: '# Refresh project on PooledTaskBroker.',
145 | preloadfilename: config.preloadfilename,
146 | preloaddirectory: config.preloaddirectory,
147 | preloadfileauthor: config.preloadfileauthor,
148 | preloadfileversion: config.preloadfileversion
149 | };
150 |
151 | // exe will fulfill only once all the inputs have fulfilled
152 | this.resourceTokenPool.q.forEach(function(project) {
153 | calls.push(
154 | deployr.io('/r/project/recycle')
155 | .share(ruser.getCookies())
156 | .delay()
157 | .data( { project: project })
158 | .end()
159 | .io('/r/project/execute/code')
160 | .share(ruser.getCookies())
161 | .delay()
162 | .data(merge(data, { project: project }))
163 | .end() );
164 | });
165 |
166 | // execute a series of sequentially chained tasks in sequence batches
167 | // without overlap. Works in conjunction with .delay() + .end() or if no
168 | // .end() is used
169 | return deployr.pipeline(calls);
170 | },
171 |
172 | /**
173 | * Release all client-side and server-side resources maintained by or on
174 | * behalf of an instance of `RBroker`.
175 | *
176 | * @method shutdown
177 | * @override
178 | * @return {Promise} A promise wrapping the resolution of either "resolve" or
179 | * "reject" callback.
180 | * @api public
181 | */
182 | shutdown: function () {
183 | var projects = this.resourceTokenPool.q.slice();
184 |
185 | this.resourceTokenPool.q = [];
186 | this.httpKeepAlive.stop();
187 | this.flush();
188 |
189 | return this.ruser.release(projects);
190 | },
191 |
192 | /**
193 | * @override
194 | * @api private
195 | */
196 | createWorker: function (task) {
197 | return PooledTaskWorker.new(task, this.ruser);
198 | }
199 | });
200 |
--------------------------------------------------------------------------------
/examples/tutorial/pooled/pooled-basics.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Example using a Pooled Task Broker
8 |
9 |
10 |
11 |
12 |
13 | Example using a Pooled Task Broker
14 |
15 |
16 | - Populate /examples/config.json with the proper values before running.
17 | - Open the browser's debug window to view print logs for this example.
18 | - View documentation for more information.
19 |
20 |
21 |
174 |
175 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | JavaScript RBroker Framework for DeployR
2 | ========================================
3 |
4 | The JavaScript RBroker Framework provides a simple yet powerful API that
5 | supports the rapid integration of R Analytics inside any browser or Node.js
6 | based application. Simply define an _RTask_, submit your task to an instance of
7 | _RBroker_ and then retrieve your task results. It really is that simple.
8 |
9 | Links
10 | -----
11 |
12 | * [Download](#downloading)
13 | * [Tutorial](https://msdn.microsoft.com/en-us/microsoft-r/deployr-rbroker-framework)
14 | * [API Documentation](http://microsoft.github.io/js-rbroker-framework/)
15 | * [Simple examples](#examples)
16 | * [Gulp, for building](#building)
17 | * [Tests](#tests)
18 | * [License](#license)
19 |
20 | Downloading
21 | ============
22 |
23 | There are a few diffrent ways to obtain the library depending on your
24 | environment and intended usage:
25 |
26 | - `$ npm install rbroker` dependent on [Node.js](http://nodejs.org/download/)
27 | - `$ git clone https://github.com/microsoft/js-rbroker-framework.git`
28 | - [Download Zip](https://github.com/microsoft/js-rbroker-framework/archive/master.zip)
29 | - Download specific [release](https://github.com/microsoft/js-rbroker-framework/releases)
30 |
31 | Environments
32 | ============
33 |
34 | - Browser and Node.js
35 |
36 | We recommend you [download and install](http://nodejs.org/download/) Node.js.
37 | It is __not__ a requirement for the browser however using the JavaScript RBroker
38 | Framework from within a server or from the command line in addition to the
39 | browser can be powerful!
40 |
41 | ### Browser
42 |
43 | If your environment is the browser the JavaScript RBroker Framework can be found
44 | here:
45 |
46 | ```
47 | ./rbroker/browser/rbroker.js
48 | ./rbroker/browser/rbroker.min.js
49 | ```
50 |
51 | ### Node.js
52 |
53 | If your environment is Node then the entire root ```./rbroker/``` directory
54 | represents the JavaScript RBroker Framework as it uses the same source for both
55 | environments.
56 |
57 | Installing
58 | ==========
59 |
60 | ### Browser
61 |
62 | Using the browser version:
63 |
64 | ```
65 | ./rbroker/browser/rbroker.js
66 | ./rbroker/browser/rbroker.min.js
67 | ```
68 |
69 | Include either one on your page in the `
74 |
75 | Or
76 |
77 |
78 |
79 | ```
80 |
81 | ### Node.js
82 |
83 | 1. [Download and install](http://nodejs.org/download/) Node.js, which includes
84 | npm. npm, which stands for _node packaged modules_, is a way to manage
85 | development dependencies through Node.js.
86 |
87 | 2. `$npm install rbroker`
88 |
89 | 3. `require` the directory:
90 |
91 | ```js
92 | var rbroker = require('rbroker');
93 | ```
94 |
95 | Examples
96 | ========
97 |
98 | The JavaScript RBroker Framework ships with a set of small examples under the
99 | __./rbroker/examples__ directory that run in both the browser and Node.js
100 | environments. The intention of the examples are to demonstrate the syntax and
101 | core areas of the JavaScript API. They are not intended to be a tutorial on how
102 | to write web applications.
103 |
104 | We encourage you to start here and customize these examples and adapt them to
105 | suit your needs as you explore the API.
106 |
107 | __./examples/tutorial:__ Introduces the three RBroker runtimes available.
108 | These runtimes are identified as:
109 |
110 | 1. Discrete Task Runtime
111 | 2. Pooled Task Runtime
112 | 3. Background Task Runtime
113 |
114 | R Analytics File Dependencies
115 | -----------------------------
116 |
117 | The R scripts and data models used by these example applications are bundled by
118 | default within the DeployR repository within the tutorial-rbroker directory
119 | owned by testuser.
120 |
121 | However, if for any reason your DeployR repository does not contain
122 | these fiels you can add them using the DeployR Repository Manager as
123 | follows:
124 |
125 | 1. Log in as testuser into the Repository Manager
126 | 2. Create a new directory called tutorial-rbroker
127 | 3. Upload each of the files found in the `./examples/analytics` directory
128 | 4. Set the access control on 5SecondNoOp.R to Public.
129 |
130 | ### Running
131 |
132 | __Browser:__
133 |
134 | - Copy the _.html_ files under `./examples` to your webserver
135 | - Copy the `./examples/config.json` file under `./examples` to your webserver
136 | - Set the DeployR endpoint and basic authentication credentials in
137 | `./examples/config.json`
138 |
139 | ***NOTE:*** The `testuser` user must first be enabled in DeployR and a password
140 | set.
141 |
142 | ```json
143 | {
144 | "endpoint": "http://dhost:port",
145 | "credentials": {
146 | "username": "testuser",
147 | "password": "YOUR_TESTUSER_PASSWORD"
148 | }
149 | }
150 | ```
151 |
152 | - Open your browser and select an example `.html` file to run. All examples
153 | simply print to stdout viewable from your browser debug console.
154 |
155 | Alternatively, you can run the examples as is without moving them via the
156 | embedded web server if you have [Node.js](http://nodejs.org/download/) installed:
157 |
158 | `$ npm install rbroker`
159 |
160 | `$ cd ./rbroker`
161 |
162 | `$ npm install`
163 |
164 | `$ npm start`
165 |
166 | Open your browser to _http://localhost:8080_ and select a example
167 | `.html` file to run. All examples simply print to stdout viewable from your
168 | browser debug console.
169 |
170 | __Node.js:__
171 |
172 | Set the DeployR endpoint and basic authentication credentials in
173 | `./examples/config.json`
174 |
175 | ***NOTE:*** The `testuser` user must first be enabled in DeployR and a password
176 | set.
177 |
178 | ```json
179 | {
180 | "endpoint": "http://dhost:port",
181 | "credentials": {
182 | "username": "testuser",
183 | "password": "YOUR_TESTUSER_PASSWORD"
184 | }
185 | }
186 |
187 | ```
188 |
189 | From the command line run one of the Node.js examples:
190 |
191 | `$ node ./examples/PATH_TO_EXAMPLE_FILE.js`
192 |
193 | Building
194 | ========
195 |
196 | This section only pertains to the _Browser_ environment.
197 |
198 | Our dev and release builds are handled by [gulp.js](http://gulpjs.com/).
199 |
200 | ### Installation
201 |
202 | 1. [Download and install](http://nodejs.org/download/) Node.js
203 | 2. `$ npm install rbroker`
204 | 3. `$ cd ./rbroker`
205 | 4. `$ npm install` This will install the development tools needed to build locally.
206 |
207 | ### Targets
208 |
209 | * `$ npm run build` - Runs a build.
210 | * `$ npm start` - Runs a build and starts a local webserver with LiveReload
211 | on `http://localhost:8080` rebuilding on file changes.
212 |
213 | ### Destination
214 |
215 | The browser build destination is located in the __./browser__ directory.
216 |
217 | Tests
218 | =====
219 |
220 | The DeployR JavaScript RBroker Framework also ships with a set of unit tests.
221 | See [here](https://github.com/microsoft/js-rbroker-framework/tree/master/test) for
222 | details.
223 |
224 | License
225 | =======
226 |
227 | Copyright (C) 2010-2016, Microsoft Corporation
228 |
229 | This program is licensed to you under the terms of Version 2.0 of the
230 | Apache License. This program is distributed WITHOUT
231 | ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
232 | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
233 | Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
234 | details.
--------------------------------------------------------------------------------
/rbroker.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var deployr = require('deployr'),
13 | DiscreteTask = require('./lib/task/discreate-task'),
14 | PooledTask = require('./lib/task/pooled-task'),
15 | BackgroundTask = require('./lib/task/background-task'),
16 | DiscreteTaskBroker = require('./lib/engine/discrete-task-broker'),
17 | PooledTaskBroker = require('./lib/engine/pooled-task-broker'),
18 | BackgroundTaskBroker = require('./lib/engine/background-task-broker');
19 |
20 | // -- expose `deployr` into the global namespace for browser convenience --
21 | if (typeof window !== 'undefined' && !window.deployr) {
22 | window.deployr = deployr;
23 | }
24 |
25 | /**
26 | * The `rbroker` global namespace object. This is the public interface for the
27 | * DeployR `RBroker`. It is used as a factory to simplify the creation of new
28 | * instances of:
29 | *
30 | * - DiscreteTaskBroker
31 | * - PooledTaskBroker
32 | * - BackgroundTaskBroker
33 | * - DiscreteTask
34 | * - PooledTask
35 | * - BackgroundTask
36 | *
37 | * @module rbroker
38 | * @for rbroker
39 | */
40 | module.exports = {
41 | /**
42 | * Expose `deployr` via the `rbroker` for convenience.
43 | *
44 | * @property deployr
45 | * @static
46 | * @api public
47 | */
48 | deployr: deployr,
49 |
50 | /**
51 | * Defines the factory for creating a DeployR-specific encoded R object to be
52 | * sent as input parameters to an R script.
53 | *
54 | * Example:
55 | * ```
56 | * var rinput = rbroker.RInput.logical('logical_name', true);
57 | * var rinput = rbroker.RInput.numeric('numeric_name', 10.5);
58 | * var rinput = rboker.RInput.integer('integer_name', 5);
59 | * var rinput = rboker.RInput.character('character_name', 'Hello');
60 | * // ect...
61 | * ```
62 | * @property RInput
63 | * @static
64 | * @api public
65 | */
66 | RInput: deployr.RInput,
67 |
68 | /**
69 | * Create an instance of an `DiscreteTaskBroker` to manage the execution of
70 | * a `DiscreteTask`.
71 | *
72 | * @method discreteTaskBroker
73 | * @static
74 | * @param {Object} [options] Configuration options.
75 | * @return {RBroker} A new instance of a `DiscreteTaskBroker`.
76 | * @api public
77 | */
78 | discreteTaskBroker: function(config) {
79 | return DiscreteTaskBroker.new(config);
80 | },
81 |
82 | /**
83 | * Create an instance of an `PooledTaskBroker` to manage the execution of
84 | * a `PooledTask`.
85 | *
86 | * @method pooledTaskBroker
87 | * @static
88 | * @param {Object} [options] Configuration options.
89 | * @return {RBroker} A new instance of a `PooledTaskBroker`.
90 | * @api public
91 | */
92 | pooledTaskBroker: function(config) {
93 | return PooledTaskBroker.new(config);
94 | },
95 |
96 | /**
97 | * Create an instance of an `BackgroundTaskBroker` to manage the execution of
98 | * a `BackgroundTask`.
99 | *
100 | * @method backgroundTaskBroker
101 | * @static
102 | * @param {Object} [options] Configuration options.
103 | * @return {RBroker} A new instance of a `BackgroundTaskBroker`.
104 | * @api public
105 | */
106 | backgroundTaskBroker: function(config) {
107 | return BackgroundTaskBroker.new(config);
108 | },
109 |
110 | /**
111 | * Create an instance of a `DiscreteTask` for an analytics Web service based
112 | * on either:
113 | *
114 | * 1. Repository-managed R script
115 | *
116 | * ```
117 | * var rtask = rbroker.discreteTask({
118 | * filename: 'regression',
119 | * directory: 'demo',
120 | * author: 'george',
121 | * version: version
122 | * // Additional Discrete Task Options...
123 | * });
124 | * ```
125 | *
126 | * 2. URL-addressable R script
127 | *
128 | * ```
129 | * var rtask = rbroker.discreteTask( {
130 | * externalsource: regressionURL
131 | * // Additional Discrete Task Options...
132 | * });
133 | * ```
134 | *
135 | * @method discreteTask
136 | * @static
137 | * @param {Object} [options] Configuration options.
138 | * @return {RTask} A new instance of a `DiscreteTask`.
139 | * @api public
140 | */
141 | discreteTask: function(config) {
142 | return DiscreteTask.new(config);
143 | },
144 |
145 | /**
146 | * Create an instance of a `PooledTask` for an analytics Web service based
147 | * on either:
148 | *
149 | * 1. Repository-managed R script
150 | *
151 | * ```
152 | * var rtask = rbroker.pooledTask({
153 | * filename: 'regression',
154 | * directory: 'demo',
155 | * author: 'george',
156 | * version: version
157 | * // Additional Pooled Task Options...
158 | * });
159 | * ```
160 | *
161 | * 2. Arbitrary block of R code
162 | *
163 | * ```
164 | * var rtask = rbroker.pooledTask({
165 | * code: codeBlock
166 | * // Additional Pooled Task Options...
167 | * });
168 | * ```
169 | *
170 | * 3. URL-addressable R script
171 | *
172 | * ```
173 | * var rtask = rbroker.pooledTask( {
174 | * externalsource: regressionURL
175 | * // Additional Pooled Task Options...
176 | * });
177 | * ```
178 | *
179 | * @method pooledTask
180 | * @static
181 | * @param {Object} [options] Configuration options.
182 | * @return {RTask} A new instance of a `PooledTask`.
183 | * @api public
184 | */
185 | pooledTask: function(config) {
186 | return PooledTask.new(config);
187 | },
188 |
189 | /**
190 | * Create an instance of a `BackgroundTask` for an analytics Web service
191 | * based on either:
192 | *
193 | * 1. Repository-managed R script
194 | *
195 | * ```
196 | * var rTask = rbroker.backgroundTask({
197 | * name: 'Sample Task',
198 | * descr: 'Sample description',
199 | * rscriptname: 'regression',
200 | * rscriptdirectory: 'demo',
201 | * rscriptauthor: 'george',
202 | * rscriptversion: version
203 | * // Additional Background Task Options...
204 | * });
205 | * ```
206 | *
207 | * 2. Arbitrary block of R code
208 | *
209 | * ```
210 | * var rtask = rbroker.backgroundTask({
211 | * name: 'Sample Task',
212 | * descr: 'Sample description',
213 | * code: codeBlock
214 | * // Additional Background Task Options...
215 | * });
216 | * ```
217 | *
218 | * 3. URL-addressable R script
219 | *
220 | * ```
221 | * var rtask = rbroker.backgroundTask( {
222 | * name: 'Sample Task',
223 | * descr: 'Sample description',
224 | * externalsource: regressionURL
225 | * // Additional Background Task Options...
226 | * });
227 | * ```
228 | *
229 | * @method backgroundTask
230 | * @static
231 | * @param {Object} [options] Configuration options.
232 | * @return {RTask} A new instance of an `BackgroundTask`.
233 | * @api public
234 | */
235 | backgroundTask: function(config) {
236 | return BackgroundTask.new(config);
237 | }
238 | };
239 |
--------------------------------------------------------------------------------
/examples/tutorial/discrete/discrete-basics.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Example using a Discrete Task Broker
8 |
9 |
10 |
11 |
12 |
13 | Example using a Discrete Task Broker
14 |
15 |
16 | - Populate /examples/config.json with the proper values before running.
17 | - Open the browser's debug window to view print logs for this example.
18 | - View documentation for more information.
19 |
20 |
21 |
169 |
170 |
--------------------------------------------------------------------------------
/gulp/tasks/header.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var gulp = require('gulp'),
13 | header = require('gulp-header'),
14 | config = require('../config.js'),
15 | pkg = config.pkg,
16 | banner = [
17 | '/*!',
18 | ' * `<%= pkg.name %>` JavaScript Client Library v<%= pkg.version %>',
19 | ' * <%= pkg.homepage %>',
20 | ' *',
21 | ' * Copyright (C) 2010-2016, Microsoft Corporation',
22 | ' * Released under the Apache License 2.0',
23 | ' * http://www.apache.org/licenses/LICENSE-2.0',
24 | ' *',
25 | ' * Includes:',
26 | ' * - superagent: https://github.com/visionmedia/superagent',
27 | ' * - ws: https://github.com/einaros/ws',
28 | ' * - D.js: http://malko.github.io/D.js',
29 | ' * - yui-lang.js: https://github.com/yui/yui3 (DeployR port)',
30 | ' *',
31 | ' * superagent',
32 | ' *',
33 | ' * Copyright (c) 2014-2015 TJ Holowaychuk ',
34 | ' * Open Source Initiative OSI - The MIT License',
35 | ' *',
36 | ' * Permission is hereby granted, free of charge, to any person obtaining',
37 | ' * a copy of this software and associated documentation files (the,',
38 | ' * "Software"), to deal in the Software without restriction, including',
39 | ' * without limitation the rights to use, copy, modify, merge, publish,',
40 | ' * distribute, sublicense, and/or sell copies of the Software, and to',
41 | ' * permit persons to whom the Software is furnished to do so, subject to',
42 | ' * the following conditions:',
43 | ' *',
44 | ' * The above copyright notice and this permission notice shall be',
45 | ' * included in all copies or substantial portions of the Software.',
46 | ' *',
47 | ' * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND',
48 | ' * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF',
49 | ' * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND',
50 | ' * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE',
51 | ' * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION',
52 | ' * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION',
53 | ' * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.',
54 | ' *',
55 | ' * ws',
56 | ' *',
57 | ' * Copyright (c) 2011 Einar Otto Stangvik ',
58 | ' * Open Source Initiative OSI - The MIT License',
59 | ' *',
60 | ' * Permission is hereby granted, free of charge, to any person obtaining ',
61 | ' * a copy of this software and associated documentation files ',
62 | ' * (the "Software"), to deal in the Software without restriction, ',
63 | ' * including without limitation the rights to use, copy, modify, merge',
64 | ' * publish, distribute, sublicense, and/or sell copies of the Software, ',
65 | ' * and to permit persons to whom the Software is furnished to do so, ',
66 | ' * subject to the following conditions:',
67 | ' *',
68 | ' * The above copyright notice and this permission notice shall be ',
69 | ' * included in all copies or substantial portions of the Software.',
70 | ' *',
71 | ' * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ',
72 | ' * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ',
73 | ' * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.',
74 | ' * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ',
75 | ' * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ',
76 | ' * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ',
77 | ' * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.',
78 | ' *',
79 | ' * D',
80 | ' *',
81 | ' * Copyright (C) 2013 Jonathan Gotti ',
82 | ' * Open Source Initiative OSI - The MIT License',
83 | ' *',
84 | ' * Permission is hereby granted, free of charge, to any person obtaining',
85 | ' * a copy of this software and associated documentation files (the,',
86 | ' * "Software"), to deal in the Software without restriction, including',
87 | ' * without limitation the rights to use, copy, modify, merge, publish,',
88 | ' * distribute, sublicense, and/or sell copies of the Software, and to',
89 | ' * permit persons to whom the Software is furnished to do so, subject to',
90 | ' * the following conditions:',
91 | ' *',
92 | ' * The above copyright notice and this permission notice shall be',
93 | ' * included in all copies or substantial portions of the Software.',
94 | ' *',
95 | ' * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND',
96 | ' * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF',
97 | ' * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND',
98 | ' * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE',
99 | ' * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION',
100 | ' * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION',
101 | ' * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.',
102 | ' *',
103 | ' * selfish',
104 | ' *',
105 | ' * Copyright 2011 Irakli Gozalishvili. All rights reserved',
106 | ' * Open Source Initiative OSI - The MIT License',
107 | ' *',
108 | ' * Permission is hereby granted, free of charge, to any person obtaining',
109 | ' * a copy of this software and associated documentation files (the,',
110 | ' * "Software"), to deal in the Software without restriction, including',
111 | ' * without limitation the rights to use, copy, modify, merge, publish,',
112 | ' * distribute, sublicense, and/or sell copies of the Software, and to',
113 | ' * permit persons to whom the Software is furnished to do so, subject to',
114 | ' * the following conditions:',
115 | ' *',
116 | ' * The above copyright notice and this permission notice shall be',
117 | ' * included in all copies or substantial portions of the Software.',
118 | ' *',
119 | ' * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND',
120 | ' * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF',
121 | ' * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND',
122 | ' * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE',
123 | ' * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION',
124 | ' * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION',
125 | ' * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.',
126 | ' *',
127 | ' * yui-lang',
128 | ' *',
129 | ' * The lang component is a DeployR port of yui-lang.js by Yahoo!',
130 | ' *',
131 | ' * Software License Agreement (BSD License)',
132 | ' * Copyright (c) 2013, Yahoo! Inc. All rights reserved.',
133 | ' *',
134 | ' * Redistribution and use of this software in source and binary forms, ',
135 | ' * with or without modification, are permitted provided that the ',
136 | ' * following conditions are met:',
137 | ' *',
138 | ' * Redistributions of source code must retain the above copyright notice,',
139 | ' * this list of conditions and the following disclaimer. Redistributions',
140 | ' * in binary form must reproduce the above copyright notice, this list of',
141 | ' * conditions and the following disclaimer in the documentation and/or ',
142 | ' * other materials provided with the distribution.',
143 | ' * ',
144 | ' * Neither the name of Yahoo! Inc. nor the names of YUI\'s contributors ',
145 | ' * may be used to endorse or promote products derived from this software ',
146 | ' * without specific prior written permission of Yahoo! Inc.',
147 | ' * ',
148 | ' * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS',
149 | ' * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ',
150 | ' * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ',
151 | ' * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ',
152 | ' * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ',
153 | ' * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ',
154 | ' * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ',
155 | ' * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ',
156 | ' * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT, ',
157 | ' * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ',
158 | ' * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.',
159 | ' *',
160 | ' * Date: <%= date.today %>',
161 | ' */',
162 | ''].join('\n');
163 |
164 | function today() {
165 | var today = new Date(),
166 | dd = today.getDate(),
167 | mm = today.getMonth() + 1, // January is 0
168 | yyyy = today.getFullYear();
169 |
170 | if(dd < 10) { dd = '0' + dd; }
171 | if(mm < 10) { mm = '0' + mm; }
172 |
173 | return yyyy + '-' + mm + '-' + dd;
174 | }
175 |
176 | /*
177 | * Task: header
178 | *
179 | * Prefix the copyright information at the top.
180 | */
181 | gulp.task('header', ['uglify'], function() {
182 | return gulp.src(config.dist + '/*.js')
183 | .pipe(header(banner, { pkg : config.pkg, date: { today: today() } } ))
184 | .pipe(gulp.dest(config.dist));
185 | });
--------------------------------------------------------------------------------
/lib/engine/rbroker-engine.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 | var deployr = require('deployr'),
12 | merge = require('merge'),
13 | Base = require('selfish').Base,
14 | EventEmitter = require('deployr/lib/emitter'),
15 | RTaskToken = require('../rtask-token'),
16 | RTaskQueue = require('../util/rtask-queue'),
17 | MAX_TASK_QUEUE_SIZE = 999;
18 |
19 | /**
20 | * Represents a high-level programming model for building DeployR-enabled
21 | * client applications. By using RBroker an application developer can focus
22 | * entirely on integrating R Analytics, while offloading the complexity of
23 | * managing client-side API task queues and server-side R session lifecycles.
24 | *
25 | * The basic programming model for working with RBroker is as follows:
26 | *
27 | * 1. Decide if the R Analytics tasks for your application should execute as:
28 | * - Discrete tasks: authentication optional, grid resources allocated at
29 | * runtime, results returned immediately, no persistence. Good for
30 | * prototyping and public facing production deployments.
31 | * - Pooled tasks: authentication required, grid resources pre-allocated,
32 | * results returned immediately, optional persistence to repository. Good
33 | * for enterprise production deployments, consistent runtime,
34 | * high-throughput environments.
35 | * - Background tasks: authentication required, grid resources allocated at
36 | * runtime, results persisted for later retrieval. Good for periodic,
37 | * scheduled or batch processing.
38 | * 2. Use the factories defined in rbroker to create an appropriate instance of
39 | * `RBroke`r.
40 | * 3. Define the R Analytics tasks for your application as one or more `RTask`.
41 | * 4. Submit your `RTask` to `RBroker` for execution.
42 | * 5. Integrate the results of your `RTask` found within `.complete()`
43 | *
44 | * @module rbroker-engine
45 | * @for rbroker
46 | */
47 | module.exports = Base.extend(EventEmitter, {
48 |
49 | /**
50 | * Represents a high-level programming model for building DeployR-enabled
51 | * client applications.
52 | *
53 | * @class
54 | * @constructor
55 | * @param {Object} [options] Configuration options.
56 | */
57 | initialize: function (config) {
58 | EventEmitter.initialize.call(this);
59 |
60 | var self = this;
61 | this.config = config;
62 | this.scope = this.config.ctx || this; // EventEmitter context
63 | this.engineStarted = false;
64 | this.parallelTaskLimit = this.config.maxConcurrentTaskLimit || 1;
65 | this.pendingLow = RTaskQueue.new(MAX_TASK_QUEUE_SIZE);
66 | this.pendingHigh = RTaskQueue.new(MAX_TASK_QUEUE_SIZE);
67 | this.resourceTokenPool = RTaskQueue.new(MAX_TASK_QUEUE_SIZE);
68 |
69 | //
70 | // Configure `deployr` client with supplied runtime settings and catch
71 | // all `io` internal errors.
72 | //
73 | deployr.configure( {
74 | host: config.host,
75 | cors: config.cors,
76 | logging: config.logging,
77 | allowSelfSignedSSLCert: config.allowSelfSignedSSLCert,
78 | events: {
79 | error: function(api, res) {
80 | this.emit('error', res);
81 | }.bind(this)
82 | }
83 | });
84 |
85 | /**
86 | * Correlate the running task counter and other runtime statistics.
87 | *
88 | * @private
89 | */
90 | this.runtime = {
91 | runningTasks: 0,
92 |
93 | totals: { success: 0, failure: 0 },
94 |
95 | stats: function(res) {
96 | return {
97 | maxConcurrency: self.maxConcurrency(),
98 | totalTasksRun: this.totals.success + this.totals.failure,
99 | totalTasksRunToSuccess: this.totals.success,
100 | totalTasksRunToFailure: this.totals.failure,
101 | totalTimeTasksOnCode: res.result.timeOnCode,
102 | totalTimeTasksOnServer: res.result.timeOnServer,
103 | totalTimeTasksOnCall: res.result.timeOnCall,
104 | task: res.task
105 | };
106 | },
107 |
108 | calibrate: function(direction) {
109 | this.runningTasks = this.runningTasks + direction;
110 |
111 | if (this.runningTasks < 0) {
112 | this.runningTasks = 0;
113 | }
114 |
115 | return this.runningTasks;
116 | }
117 | };
118 |
119 | // -- emitted by subclasses indicating workers can begin work --
120 | this.on('ready', function() {
121 | this.engineStarted = true;
122 |
123 | // Flush the queues to force work.
124 | for (var i = 0; i < this.parallelTaskLimit; i++) {
125 | if (!this.isIdle()) {
126 | this.run();
127 | } else {
128 | break;
129 | }
130 | }
131 | });
132 | },
133 |
134 | /**
135 | * Test the `/r/server/info` endpoint, expect HTTP 200 othwrise
136 | *
137 | * @param {Function} The optional callback
138 | * @api public
139 | */
140 | validateEndpoint: function(cb) {
141 | return deployr.io('/r/server/info')
142 | .ctx(this)
143 | .timeout(1000 * 10)
144 | .end(cb);
145 | },
146 |
147 | owner: function() {
148 | return this.ruser;
149 | },
150 |
151 | /**
152 | * Launch an `RTaskAppSimulator` simulation. The `RTask` defined by your
153 | * simulation will be automatically executed by the current instance of
154 | * `RBroker`.
155 | *
156 | * Make sure to register your `.complete()`, `.error()`, `.idle()`, and
157 | * `.progress()` listeners before starting your simulation in order to
158 | * receive asynchronous callbacks in your application when `RTask` complete
159 | * and/or to receive runtime summary statistics from `RBroker` as the
160 | * simulation proceeds.
161 | *
162 | * @method simulateApp
163 | * @param {Object} The Application Simulator that implements the
164 | * `simulateApp(RBroker)` method.
165 | * @api public
166 | */
167 | simulateApp: function(appSimulator) {
168 | if (appSimulator) {
169 | if (typeof appSimulator.simulateApp === 'function') {
170 | appSimulator.simulateApp(this);
171 | } else {
172 | throw new Error('ReferenceError: "simulateApp" is not defined');
173 | }
174 | }
175 | },
176 |
177 | /**
178 | * Submit an `RTask` for execution under the control of `RBroker`. If
179 | * priority is indicated, priority tasks are automatically moved to the front
180 | * of the queue, ahead of all standard tasks that are already pending
181 | * execution by the broker.
182 | *
183 | * @method submit
184 | * @param {RTask} The `RTask` associated with this submission.
185 | * @param {Boolean} (optional) Mark this task as having priority.
186 | * @return {RTaskToken} The handle to an `RTask` live on an `RBroker`.
187 | * @api public
188 | */
189 | submit: function(task, priority) {
190 | // `worker` is a defred object. It is either currently working or queued
191 | // up with an appointment to work in the future once `run()` is called
192 | var worker = this.createWorker(task),
193 | taskToken = RTaskToken.new(worker);
194 |
195 | if (priority) {
196 | this.pendingHigh.offer(worker);
197 | } else {
198 | this.pendingLow.offer(worker);
199 | }
200 |
201 | // start the task work
202 | this.run();
203 |
204 | return taskToken;
205 | },
206 |
207 | /**
208 | * Returns the task execution concurrency levels enforced for this instance
209 | * of `RBroker`.
210 | *
211 | * @method maxConcurrency
212 | * @return {Number} The task execution concurrency levels.
213 | * @api public
214 | */
215 | maxConcurrency: function() {
216 | return this.parallelTaskLimit;
217 | },
218 |
219 | /**
220 | * Flushes all pending `RTask` from queues maintained by `RBroker`. Flushing
221 | * `RTask` queues ensures that queued tasks will not be executed by
222 | * `RBroker`.
223 | *
224 | * @method flush
225 | * @return {Object} Object literal containing `RBroker` status indicating
226 | * the number of currently queued and executing `RTask`.
227 | * @api public
228 | */
229 | flush: function() {
230 | this.pendingHigh.clear();
231 | this.pendingLow.clear();
232 |
233 | return this.status();
234 | },
235 |
236 | /**
237 | * Indicates if current `RBroker` instance is still connected to the DeployR
238 | * server. A connection may be lost for a number of reasons, for example, due
239 | * to a droppeed network connection between client and server or if the
240 | * DeployR server itself goes down.
241 | *
242 | * @method isConnected
243 | * @return {Promise} A promise with a `Boolean` value connection status.
244 | * @api public
245 | */
246 | isConnected: function() {
247 | var test = this.ruser ? this.ruser.io('/r/user/about') :
248 | deployr.io('/r/server/info')
249 |
250 | //
251 | // (authenticated | anonymous) checks
252 | // authenticated - Test connection to authenticated HTTP session
253 | // anonymous - Test the that the server is up.
254 | //
255 | return test
256 | .global(false) // supress global error events for this `io`
257 | .promise()
258 | .success(function() {
259 | return true;
260 | })
261 | .error(function() {
262 | return false;
263 | });
264 | },
265 |
266 | /**
267 | * Returns status indicating current `RTask` activity on `RBroker`.
268 | *
269 | *
270 | * @method status
271 | * @return {Object} Object literal containing `RBroker` status indicating the
272 | * number of currently queued and executing `RTask`.
273 | * @api public
274 | */
275 | status: function() {
276 | // Pending tasks include all tasks on high and low priority queues.
277 | var pending = this.pendingHigh.size() + this.pendingLow.size(),
278 | executing = this.parallelTaskLimit - this.runtime.runningTasks,
279 | idle = this.isIdle();
280 |
281 | return {
282 | pending: pending,
283 | executing: executing,
284 | idle: idle,
285 | toString: function() {
286 | return '\nRBrokerStatus: [ pending = "' + pending + '" ] ' +
287 | '[ executing = "' + executing + '" ] ' +
288 | '[ idle = "' + idle + '" ]\n';
289 | }
290 | };
291 | },
292 |
293 | /**
294 | * Used to determine if an `RBroker` instance is idle which can be
295 | * particularly useful ahead of calls to `shutdown()`. Another options is to
296 | * bind a listener to the `.isIdle()` method.
297 | *
298 | * @method isIdle
299 | * @return {Boolean} If the `RBroker` instance is idle.
300 | * @api public
301 | */
302 | isIdle: function() {
303 | return (this.runtime.runningTasks === 0 &&
304 | this.pendingLow.isEmpty() && this.pendingHigh.isEmpty());
305 | },
306 |
307 | /**
308 | * An initialization lifecycle listener fired during construction.
309 | *
310 | * @method error
311 | * @param {Function} The callback function.
312 | * @return {RBroker} The refrence to `this` `RBroker` used for chaining.
313 | * @api public
314 | */
315 | ready: function(fn) {
316 | this.on('ready', fn);
317 |
318 | return this;
319 | },
320 |
321 | /**
322 | * A failure listener for all tasks submitted on behalf of `this` `RBroker`.
323 | *
324 | * @method error
325 | * @param {Function} The callback function.
326 | * @return {RBroker} The refrence to `this` `RBroker` used for chaining.
327 | * @api public
328 | */
329 | error: function(fn) {
330 | this.on('error', fn);
331 |
332 | return this;
333 | },
334 |
335 | /**
336 | * A notification listener indicating warnings about dubious runtime behavior
337 | * submitted on behalf of `this` `RBroker`.
338 | *
339 | * @method warning
340 | * @param {Function} The callback function.
341 | * @return {RBroker} The refrence to `this` `RBroker` used for chaining.
342 | * @api public
343 | */
344 | warning: function(fn) {
345 | this.on('warning', fn);
346 |
347 | return this;
348 | },
349 |
350 | /**
351 | * A completion listener for all tasks submitted on behalf of `this`
352 | * `RBroker`.
353 | *
354 | * @method complete
355 | * @param {Function} The callback function.
356 | * @return {RBroker} The refrence to `this` `RBroker` used for chaining.
357 | * @api public
358 | */
359 | complete: function(fn) {
360 | this.on('complete', fn);
361 |
362 | return this;
363 | },
364 |
365 | /**
366 | * A notification listener indicating that `this` `Rbroker` is still active
367 | * and there are currently no `RTasks` running or in the wait queue.
368 | *
369 | * @method idle
370 | * @param {Function} The callback function.
371 | * @return {RBroker} The refrence to `this` `RBroker` used for chaining.
372 | * @api public
373 | */
374 | idle: function(fn) {
375 | this.on('idle', fn);
376 |
377 | return this;
378 | },
379 |
380 | /**
381 | * A notification listener for `RBroker` runtime statistics of a `RTask`.
382 | *
383 | * @method progress
384 | * @param {Function} The callback function.
385 | * @return {RBroker} The refrence to `this` `RBroker` used for chaining.
386 | * @api public
387 | */
388 | progress: function(fn) {
389 | this.on('progress', fn);
390 |
391 | return this;
392 | },
393 |
394 | /**
395 | * A notification listener for `RBroker` runtime statistics of a `RTask`.
396 | *
397 | * @method progress
398 | * @param {Function} The callback function.
399 | * @return {RBroker} The refrence to `this` `RBroker` used for chaining.
400 | * @api public
401 | */
402 | start: function(fn) {
403 | this.on('start', fn);
404 |
405 | return this;
406 | },
407 |
408 | /**
409 | * Do the task work.
410 | *
411 | * @api private
412 | */
413 | run: function() {
414 | var self = this, nextWorker, resourceToken;
415 |
416 | if (this.engineStarted &&
417 | !this.resourceTokenPool.isEmpty() &&
418 | (!this.pendingLow.isEmpty() || !this.pendingHigh.isEmpty())) {
419 |
420 | nextWorker = this.pendingHigh.take() || this.pendingLow.take();
421 |
422 | this.runtime.calibrate(+1);
423 |
424 | this.emit('start', nextWorker.task);
425 |
426 | nextWorker.work(this.resourceTokenPool.take()) // start working....
427 | .then(function(res) {
428 | self.runtime.totals.success++;
429 | self.emit('progress', self.runtime.stats(res));
430 | self.emit('complete', res.task, res.result);
431 |
432 | // -- individual RTask notifications --
433 | this.resolve(res);
434 | }.bind(nextWorker), function(err) {
435 | var res = {
436 | task: this.task,
437 | result: { // task error result
438 | id: null,
439 | type: this.task.type,
440 | success: false,
441 | timeOnCode: 0,
442 | timeOnServer: 0,
443 | timeOnCall: 0,
444 | failure: err
445 | }
446 | };
447 |
448 | self.runtime.totals.failure++;
449 | self.emit('progress', self.runtime.stats(res));
450 | self.emit('error', merge(err, { task: this.task }));
451 | self.emit('complete', res.task, res.result);
452 |
453 | // -- individual RTask notifications --
454 | this.reject(merge(err, { task: this.task }));
455 | }.bind(nextWorker))
456 | .ensure(function(v) { // finally
457 | self.runtime.calibrate(-1);
458 | self.resourceTokenPool.offer(this.resourceToken);
459 | self.run();
460 |
461 | // -- notify all tasks submitted have came to completion --
462 | if (self.isIdle()) { self.emit('idle'); }
463 | }.bind(nextWorker));
464 | }
465 | }
466 | });
--------------------------------------------------------------------------------
/test/specs/discrete-task-broker-test.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var test = require('tape'),
13 | merge = require('merge'),
14 | rbroker = require('../../rbroker'),
15 | fixtures = require('../fixtures/discrete');
16 |
17 | console.log('A DiscreteTaskBroker\n');
18 |
19 | /**
20 | * Test "good" endpoint.
21 | */
22 | test('should successfully initialize with good endpoint address', function(t) {
23 | t.plan(1);
24 |
25 | var dBroker,
26 | brokerConfig = {
27 | host: fixtures.endpoint,
28 | credentials: fixtures.credentials,
29 | logging: false
30 | };
31 |
32 | dBroker = rbroker.discreteTaskBroker(brokerConfig)
33 | .error(function(err) {
34 | t.fail('dBroker initialization failed "' + err.get('error') + '"');
35 | })
36 | .ready(function() {
37 | t.pass('dBroker initialization success');
38 | dBroker.shutdown().then(function() {}, function(err) {
39 | t.fail('dBroker.shutdown failed: ' + err.get('error'));
40 | });
41 | });
42 | });
43 |
44 | /**
45 | * Test "bad" endpoint.
46 | */
47 | test('should fail initialization with bad endpoint address', function(t) {
48 | t.plan(1);
49 |
50 | var dBroker,
51 | brokerConfig = {
52 | host: fixtures.BAD_ENDPOINT,
53 | credentials: fixtures.credentials,
54 | logging: false
55 | };
56 |
57 | dBroker = rbroker.discreteTaskBroker(brokerConfig)
58 | .error(function(err) {
59 | t.pass('dBroker initialization failed "' + err.get('error') + '"');
60 | })
61 | .ready(function() {
62 | t.pass('dBroker initialization success but should have failed');
63 | dBroker.shutdown().then(function() {}, function(err) {
64 | t.fail('dBroker.shutdown failed: ' + err.get('error'));
65 | });
66 | });
67 | });
68 |
69 | /**
70 | * Test anonymous DiscreteBrokerfixtures.
71 | */
72 | test('should successfully execute using an anonymous `Discrete Task Broker`', function(t) {
73 | t.plan(3);
74 |
75 | var dBroker,
76 | brokerConfig = {
77 | host: fixtures.endpoint,
78 | logging: false
79 | };
80 |
81 | dBroker = rbroker.discreteTaskBroker(brokerConfig)
82 | .complete(function(rTask, rTaskResult) {
83 | t.notEqual(rTaskResult, null, 'rTaskResult');
84 | t.ok(rTaskResult.success, 'rTaskResult.success');
85 | })
86 | .error(function(err) {
87 | var msg = err.task ? 'dBroker.submit(rTask) failed: ' :
88 | 'dBroker failed: ';
89 |
90 | t.fail(msg + err.get('error'));
91 | })
92 | .idle(function() {
93 | dBroker.shutdown().then(function() {
94 | t.pass('dBroker.shutdown success');
95 | }, function(err) {
96 | t.fail('dBroker.shutdown failed: ' + err.get('error'));
97 | });
98 | });
99 |
100 | dBroker.submit(rbroker.discreteTask({
101 | filename: 'Histogram of Auto Sales',
102 | directory: 'root',
103 | author: 'testuser'
104 | }));
105 | });
106 |
107 | /**
108 | * Test authenticated DiscreteBrokerfixtures.
109 | */
110 | test('should successfully execute using authenticated `Discrete Task Broker`', function(t) {
111 | t.plan(3);
112 |
113 | var dBroker,
114 | brokerConfig = {
115 | host: fixtures.endpoint,
116 | credentials: fixtures.credentials,
117 | logging: false
118 | };
119 |
120 | dBroker = rbroker.discreteTaskBroker(brokerConfig)
121 | .complete(function(rTask, rTaskResult) {
122 | t.notEqual(rTaskResult, null, 'rTaskResult');
123 | t.ok(rTaskResult.success, 'rTaskResult.success');
124 | })
125 | .error(function(err) {
126 | var msg = err.task ? 'dBroker.submit(rTask) failed: ' :
127 | 'dBroker failed: ';
128 |
129 | t.notOk(true, msg + err.get('error'));
130 | })
131 | .idle(function() {
132 | dBroker.shutdown().then(function() {
133 | t.pass('dBroker.shutdown success');
134 | }, function(err) {
135 | t.fail('dBroker.shutdown failed: ' + err.get('error'));
136 | });
137 | });
138 |
139 | dBroker.submit(rbroker.discreteTask({
140 | filename: 'Histogram of Auto Sales',
141 | directory: 'root',
142 | author: 'testuser'
143 | }));
144 | });
145 |
146 | /**
147 | * Test authenticated DiscreteBrokerConfig using bad credentials.
148 | */
149 | test('should not authenticate with bad credentials for `Discrete Task Broker`', function(t) {
150 | t.plan(1);
151 |
152 | var brokerConfig = {
153 | host: fixtures.endpoint,
154 | credentials: fixtures.BAD_CREDENTIALS,
155 | logging: false
156 | },
157 | dBroker = rbroker.discreteTaskBroker(brokerConfig)
158 | .ready(function() {
159 | dBroker.shutdown().then(function() {}, function(err) {
160 | t.fail('dBroker.shutdown failed: ' + err.get('error'));
161 | })
162 | .ensure(function() {
163 | t.fail('authentication successful but should not have been');
164 | });
165 | })
166 | .error(function(err) {
167 | t.equal(err.get('errorCode'), 940, err.get('error'));
168 | });
169 | });
170 |
171 | /**
172 | * Test DiscreteBrokerConfig using non-default max concurrency.
173 | */
174 | test('should successfully execute `Discrete Task Broker` using non-default max concurrency', function(t) {
175 | t.plan(3);
176 |
177 | var dBroker,
178 | MAX_CONCURRENCY = 10,
179 | brokerConfig = {
180 | host: fixtures.endpoint,
181 | credentials: fixtures.credentials,
182 | maxConcurrentTaskLimit: MAX_CONCURRENCY,
183 | logging: false
184 | };
185 |
186 | dBroker = rbroker.discreteTaskBroker(brokerConfig)
187 | .complete(function(rTask, rTaskResult) {
188 | t.notEqual(rTaskResult, null, 'rTaskResult');
189 | t.ok(rTaskResult.success, 'rTaskResult.success');
190 | })
191 | .error(function(err) {
192 | var msg = err.task ? 'dBroker.submit(rTask) failed: ' :
193 | 'dBroker failed: ';
194 |
195 | t.notOk(true, msg + err.get('error'));
196 | })
197 | .idle(function() {
198 | dBroker.shutdown().then(function() {
199 | t.pass('dBroker.shutdown success');
200 | }, function(err) {
201 | t.fail('dBroker.shutdown failed: ' + err.get('error'));
202 | });
203 | });
204 |
205 | dBroker.submit(rbroker.discreteTask({
206 | filename: 'Histogram of Auto Sales',
207 | directory: 'root',
208 | author: 'testuser'
209 | }));
210 | });
211 |
212 | /**
213 | * Test "good" RTask execution with execution-token.
214 | */
215 | test('should successfully execute `RTask` using `execution-token`', function(t) {
216 | t.plan(4);
217 |
218 | var dBroker, rTask,
219 | executionToken = 'testTaskExecutionWithTokenStandardQueueGood',
220 | brokerConfig = {
221 | host: fixtures.endpoint,
222 | credentials: fixtures.credentials,
223 | logging: false
224 | };
225 |
226 | dBroker = rbroker.discreteTaskBroker(brokerConfig)
227 | .complete(function(rTask, rTaskResult) {
228 | t.notEqual(rTaskResult, null, 'rTaskResult');
229 | t.ok(rTaskResult.success, 'rTaskResult.success');
230 | t.equal(rTask.token, executionToken, 'execution-token');
231 | })
232 | .error(function(err) {
233 | t.fail(err.task ? 'dBroker.submit(rTask) failed: ' :
234 | 'dBroker failed: ');
235 | })
236 | .idle(function() {
237 | dBroker.shutdown().then(function() {
238 | t.pass('dBroker.shutdown success');
239 | }, function(err) {
240 | t.fail('dBroker.shutdown failed: ' + err.get('error'));
241 | });
242 | });
243 |
244 | rTask = rbroker.discreteTask({
245 | filename: 'Histogram of Auto Sales',
246 | directory: 'root',
247 | author: 'testuser'
248 | });
249 |
250 | rTask.token = executionToken;
251 |
252 | dBroker.submit(rTask);
253 | });
254 |
255 | /**
256 | * Test "good" RTask priority execution with execution-token.
257 | */
258 | test('should successfully execute priority `RTask` using `execution-token`', function(t) {
259 | t.plan(4);
260 |
261 | var dBroker, rTask,
262 | executionToken = 'testTaskExecutionWithTokenPriorityQueueGood',
263 | brokerConfig = {
264 | host: fixtures.endpoint,
265 | credentials: fixtures.credentials,
266 | logging: false
267 | };
268 |
269 | dBroker = rbroker.discreteTaskBroker(brokerConfig)
270 | .complete(function(rTask, rTaskResult) {
271 | t.notEqual(rTaskResult, null, 'rTaskResult');
272 | t.ok(rTaskResult.success, 'rTaskResult.success');
273 | t.equal(rTask.token, executionToken, 'execution-token');
274 | })
275 | .error(function(err) {
276 | t.fail(err.task ? 'dBroker.submit(rTask) failed: ' :
277 | 'dBroker failed: ');
278 | })
279 | .idle(function() {
280 | dBroker.shutdown().then(function() {
281 | t.pass('dBroker.shutdown success');
282 | }, function(err) {
283 | t.fail('dBroker.shutdown failed: ' + err.get('error'));
284 | });
285 | });
286 |
287 | rTask = rbroker.discreteTask({
288 | filename: 'Histogram of Auto Sales',
289 | directory: 'root',
290 | author: 'testuser'
291 | });
292 |
293 | rTask.token = executionToken;
294 |
295 | dBroker.submit(rTask, true);
296 | });
297 |
298 | /**
299 | * Test multiple "good" RTask executions distributed across the standard and
300 | * priority task queues.
301 | */
302 |
303 | test('should execute multiple "good" RTask executions distributed across standard and priority task queues', function(t) {
304 | var dBroker,
305 | executionToken = 'testMultipleTaskExecutionWithMixedPriority',
306 | rTasks = [],
307 | priorityTaskTokens = [],
308 | standardTaskTokens = [],
309 | rTaskResults = [],
310 | priorityTask = true,
311 | multipleTaskTestSize = 10,
312 | brokerConfig = {
313 | host: fixtures.endpoint,
314 | credentials: fixtures.credentials,
315 | maxConcurrentTaskLimit: 10,
316 | logging: false
317 | };
318 |
319 | t.plan(4 + multipleTaskTestSize);
320 |
321 | dBroker = rbroker.discreteTaskBroker(brokerConfig)
322 | .complete(function(rTask, rTaskResult) {
323 | rTaskResults.push(rTaskResult);
324 | })
325 | .error(function(err) {
326 | var msg = err.task ? 'dBroker.submit(rTask) failed: ' :
327 | 'dBroker failed: ';
328 | t.fail(msg + err.get('error'));
329 | })
330 | .idle(function() {
331 | t.equal(rTasks.length, multipleTaskTestSize,
332 | 'rTasks and multipleTaskTestSize are the same size');
333 | t.equal(standardTaskTokens.length + priorityTaskTokens.length,
334 | multipleTaskTestSize, 'standardTaskTokens and ' +
335 | 'priorityTaskTokens are the same size');
336 | t.equal(rTaskResults.length, multipleTaskTestSize,
337 | 'rTaskResults and multipleTaskTestSize are the same size');
338 | rTaskResults.forEach(function(result) {
339 | t.ok(result.success, 'rTask ran to success');
340 | });
341 |
342 | dBroker.shutdown().then(function() {
343 | t.pass('dBroker.shutdown success');
344 | }, function(err) {
345 | t.fail('dBroker.shutdown failed: ' + err.get('error'));
346 | });
347 | }); // ideal
348 |
349 | for (var i = 0; i < multipleTaskTestSize; i++) {
350 | var rTask = rbroker.discreteTask({
351 | filename: 'Histogram of Auto Sales',
352 | directory: 'root',
353 | author: 'testuser'
354 | }),
355 | rTaskToken;
356 |
357 | rTasks.push(rTask);
358 | rTaskToken = dBroker.submit(rTask, priorityTask);
359 |
360 | if (priorityTask) {
361 | priorityTaskTokens.push(rTaskToken);
362 | } else {
363 | standardTaskTokens.push(rTaskToken);
364 | }
365 |
366 | // Flip priority flag to altenate task type.
367 | priorityTask = !priorityTask;
368 | }
369 | });
370 |
371 | /**
372 | * Test "bad" RTask execution with execution-token.
373 | */
374 | test('should unsuccessfully execute RTask with execution-token using "bad" values', function(t) {
375 | t.plan(4);
376 |
377 | var dBroker, rTask,
378 | executionToken = 'testTaskExecutionWithTokenBadScript',
379 | MAX_CONCURRENCY = 10,
380 | brokerConfig = {
381 | host: fixtures.endpoint,
382 | credentials: fixtures.credentials,
383 | maxConcurrentTaskLimit: MAX_CONCURRENCY,
384 | logging: false
385 | };
386 |
387 | dBroker = rbroker.discreteTaskBroker(brokerConfig)
388 | .complete(function(rTask, rTaskResult) {
389 | t.notEqual(rTaskResult, null, 'rTaskResult');
390 | t.notOk(rTaskResult.success, '!rTaskResult.success');
391 | t.equal(rTask.token, executionToken, 'execution-token');
392 | })
393 | .error(function(err) {
394 | //
395 | // suppress valid task submit errors however non-task execution
396 | // errors should fail the test
397 | //
398 | if (!err.task) {
399 | t.fail('dBroker failed: ' + err.get('error'));
400 | }
401 | })
402 | .idle(function() {
403 | dBroker.shutdown().then(function() {
404 | t.pass('dBroker.shutdown success');
405 | }, function(err) {
406 | t.fail('dBroker.shutdown failed: ' + err.get('error'));
407 | });
408 | });
409 |
410 | rTask = rbroker.discreteTask({
411 | filename: fixtures.BAD_SCRIPT_NAME,
412 | directory: fixtures.BAD_DIR_NAME,
413 | author: fixtures.BAD_AUTHOR_NAME
414 | });
415 |
416 | rTask.token = executionToken;
417 |
418 | dBroker.submit(rTask);
419 | });
420 |
421 | /**
422 | * Test RTask execution with "good" task execution options.
423 | */
424 | test('should successfully execute RTask with "good" task execution options', function(t) {
425 | t.plan(4);
426 |
427 | var dBroker, rTask,
428 | executionToken = 'testTaskExecutionWithToken',
429 | brokerConfig = {
430 | host: fixtures.endpoint,
431 | credentials: fixtures.credentials
432 | };
433 |
434 | dBroker = rbroker.discreteTaskBroker(brokerConfig)
435 | .complete(function(rTask, rTaskResult) {
436 | t.notEqual(rTaskResult, null, 'rTaskResult');
437 | t.ok(rTaskResult.success, 'rTaskResult.success');
438 | t.equal(rTask.token, executionToken, 'execution-token');
439 | })
440 | .error(function(err) {
441 | var msg = err.task ? 'dBroker.submit(rTask) failed: ' :
442 | 'dBroker failed: ';
443 | t.fail(msg + err.get('error'));
444 | })
445 | .idle(function() {
446 | dBroker.shutdown().then(function() {
447 | t.pass('dBroker.shutdown success');
448 | }, function(err) {
449 | t.fail('dBroker.shutdown failed: ' + err.get('error'));
450 | });
451 | });
452 |
453 | rTask = rbroker.discreteTask(merge({
454 | filename: 'Histogram of Auto Sales',
455 | directory: 'root',
456 | author: 'testuser'
457 | }, fixtures.good()));
458 |
459 | rTask.token = executionToken;
460 |
461 | dBroker.submit(rTask);
462 | });
463 |
464 | /**
465 | * Test RTask execution with "bad" task execution options.
466 | */
467 | test('should unsuccessfully execute RTask with "bad" task execution options', function(t) {
468 | t.plan(3);
469 |
470 | var dBroker,
471 | brokerConfig = {
472 | host: fixtures.endpoint,
473 | credentials: fixtures.credentials
474 | };
475 |
476 | dBroker = rbroker.discreteTaskBroker(brokerConfig)
477 | .complete(function(rTask, rTaskResult) {
478 | t.notEqual(rTaskResult, null, 'rTaskResult');
479 | t.notOk(rTaskResult.success, '!rTaskResult.success');
480 | })
481 | .error(function(err) {
482 | //
483 | // suppress valid task submit errors however non-task execution
484 | // errors should fail the test
485 | //
486 | if (!err.task) {
487 | t.fail('dBroker failed: ' + err.get('error'));
488 | }
489 | })
490 | .idle(function() {
491 | dBroker.shutdown().then(function() {
492 | t.pass('dBroker.shutdown success');
493 | }, function(err) {
494 | t.fail('dBroker.shutdown failed: ' + err.get('error'));
495 | });
496 | });
497 |
498 | dBroker.submit(rbroker.discreteTask(merge({
499 | filename: 'Histogram of Auto Sales',
500 | directory: 'root',
501 | author: 'testuser'
502 | }, fixtures.bad())));
503 | });
504 |
--------------------------------------------------------------------------------
/test/specs/background-task-broker-test.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (C) 2010-2016, Microsoft Corporation
3 | *
4 | * This program is licensed to you under the terms of Version 2.0 of the
5 | * Apache License. This program is distributed WITHOUT
6 | * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
7 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
8 | * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more
9 | * details.
10 | */
11 |
12 | var test = require('tape'),
13 | when = require('when'),
14 | merge = require('merge'),
15 | rbroker = require('../../rbroker'),
16 | fixtures = require('../fixtures/job');
17 |
18 | console.log('A BackgroundTaskBroker\n');
19 |
20 | /**
21 | * Teardown job tests
22 | */
23 | function teardown(t, bgBroker, jobIDs) {
24 | var promises = [];
25 |
26 | jobIDs.forEach(function(jobID) {
27 | promises.push(fixtures.deleteJobArtifacts(bgBroker.owner(), jobID));
28 | });
29 |
30 | when.all(promises).then()
31 | .ensure(function() {
32 | bgBroker.shutdown().then(function() {
33 | t.pass('bgBroker.shutdown success');
34 | }, function(err) {
35 | t.fail('bgBroker.shutdown failed: ' + err.get('error'));
36 | });
37 | });
38 | }
39 |
40 | /**
41 | * Test "good" endpoint.
42 | */
43 | test('should successfully initialize with good endpoint address', function(t) {
44 | t.plan(1);
45 |
46 | var bgBroker = null,
47 | brokerConfig = {
48 | host: fixtures.endpoint,
49 | credentials: fixtures.credentials
50 | };
51 |
52 | bgBroker = rbroker.backgroundTaskBroker(brokerConfig)
53 | .error(function(err) {
54 | t.fail('bgBroker initialization failed "' + err.get('error') + '"');
55 | })
56 | .ready(function() {
57 | t.pass('bgBroker initialization success');
58 | bgBroker.shutdown().then(function() {}, function(err) {
59 | t.fail('bgBroker.shutdown failed: ' + err.get('error'));
60 | });
61 | });
62 | });
63 |
64 | /**
65 | * Test "bad" endpoint.
66 | */
67 | test('should fail initialization with bad endpoint address', function(t) {
68 | t.plan(1);
69 |
70 | var bgBroker = null,
71 | brokerConfig = {
72 | host: fixtures.BAD_ENDPOINT,
73 | credentials: fixtures.credentials
74 | };
75 |
76 | bgBroker = rbroker.backgroundTaskBroker(brokerConfig)
77 | .error(function(err) {
78 | t.pass('bgBroker initialization failed "' + err.get('error') + '"');
79 | })
80 | .ready(function() {
81 | t.pass('bgBroker initialization success but should have failed');
82 | bgBroker.shutdown().then(function() {}, function(err) {
83 | t.fail('bgBroker.shutdown failed: ' + err.get('error'));
84 | });
85 | });
86 | });
87 |
88 | /**
89 | * Test authenticated BackgroundBrokerfixtures.
90 | */
91 | test('should authenticate with `Background Broker Config`', function(t) {
92 | t.plan(5);
93 |
94 | var brokerConfig = {
95 | host: fixtures.endpoint,
96 | credentials: fixtures.credentials,
97 | logging: false
98 | },
99 | jobID = null,
100 | bgBroker = null,
101 | rTask = null;
102 |
103 | bgBroker = rbroker.backgroundTaskBroker(brokerConfig)
104 | .complete(function(rTask, rTaskResult) {
105 | jobID = rTaskResult.id;
106 |
107 | t.notEqual(rTaskResult, null, 'rTaskResult');
108 | t.ok(rTaskResult.success, 'rTaskResult.success');
109 | t.notEqual(jobID, null, 'jobID');
110 |
111 | fixtures.verifyJobExitStatus(bgBroker.owner(), jobID, 'Completed')
112 | .then(function() {
113 | t.pass('jobCompleted');
114 | }, function(err) {
115 | t.fail('jobCompleted: ' + err);
116 | })
117 | .ensure(function() {
118 | bgBroker.shutdown().then(function() {
119 | t.pass('bgBroker.shutdown success');
120 | }, function(err) {
121 | t.fail('bgBroker.shutdown failed: ' + err.get('error'));
122 | });
123 | });
124 | })
125 | .error(function(err) {
126 | var msg = err.task ? 'bgBroker.submit(rTask) failed: ' :
127 | 'bgBroker failed: ';
128 |
129 | t.notOk(false, msg + err.get('error'));
130 | });
131 |
132 | bgBroker.submit(rbroker.backgroundTask({
133 | name: 'testConfigAuthenticationGoodCredentials',
134 | descr: 'Background Task',
135 | rscriptname: 'Histogram of Auto Sales',
136 | rscriptdirectory: 'root',
137 | rscriptauthor: 'testuser'
138 | }));
139 | });
140 |
141 | /**
142 | * Test authenticated BackgroundBrokerConfig using bad credentials.
143 | */
144 | test('should not authenticate with `Background Broker Config`', function(t) {
145 | t.plan(1);
146 |
147 | var brokerConfig = {
148 | host: fixtures.endpoint,
149 | credentials: fixtures.BAD_CREDENTIALS,
150 | logging: false
151 | },
152 | bgBroker = null;
153 |
154 | bgBroker = rbroker.backgroundTaskBroker(brokerConfig)
155 | .ready(function() {
156 | bgBroker.shutdown().then(function() {}, function(err) {
157 | t.fail('bgBroker.shutdown failed: ' + err.get('error'));
158 | })
159 | .ensure(function() {
160 | t.fail('authentication successful but should not have been');
161 | });
162 | })
163 | .error(function(err) {
164 | t.pass('rbroker.backgroundTaskBroker failed: ' + err.get('error'));
165 | });
166 | });
167 |
168 | /**
169 | * Test RTask execution with `execution-token`.
170 | */
171 | test('should execute RTask successfully with `execution-token`', function(t) {
172 | t.plan(6);
173 |
174 | var brokerConfig = {
175 | host: fixtures.endpoint,
176 | credentials: fixtures.credentials,
177 | logging: false
178 | },
179 | executionToken = 'testTaskExecutionWithTokenStandardQueueGood',
180 | jobID = null,
181 | bgBroker = null,
182 | rTask = null;
183 |
184 | bgBroker = rbroker.backgroundTaskBroker(brokerConfig)
185 | .complete(function(rTask, rTaskResult) {
186 | jobID = rTaskResult.id;
187 |
188 | t.notEqual(rTaskResult, null, 'rTaskResult');
189 | t.ok(rTaskResult.success, 'rTaskResult.success');
190 | t.equal(rTask.token, executionToken, 'execution-token');
191 | t.notEqual(jobID, null, 'jobID');
192 |
193 | fixtures.verifyJobExitStatus(bgBroker.owner(), jobID, 'Completed')
194 | .then(function() {
195 | t.pass('jobCompleted');
196 | }, function(err) {
197 | t.fail('jobCompleted: ' + err);
198 | })
199 | .ensure(function() {
200 | teardown(t, bgBroker, [jobID]);
201 | });
202 | })
203 | .error(function(err) {
204 | var msg = err.task ? 'bgBroker.submit(rTask) failed: ' :
205 | 'bgBroker failed: ';
206 |
207 | t.notOk(false, msg + err.get('error'));
208 | });
209 |
210 | rTask = rbroker.backgroundTask({
211 | name: executionToken,
212 | descr: 'Background Task',
213 | rscriptname: 'Histogram of Auto Sales',
214 | rscriptdirectory: 'root',
215 | rscriptauthor: 'testuser'
216 | });
217 |
218 | rTask.token = executionToken;
219 |
220 | bgBroker.submit(rTask);
221 | });
222 |
223 | /**
224 | * Test BackgroundBrokerConfig RTask priority execution with `execution-token`.
225 | */
226 | test('should execute priority RTask successfully with `execution-token`', function(t) {
227 | t.plan(6);
228 |
229 | var brokerConfig = {
230 | host: fixtures.endpoint,
231 | credentials: fixtures.credentials,
232 | logging: false
233 | },
234 | executionToken = 'testTaskExecutionWithTokenPriorityQueueGood',
235 | jobID = null,
236 | bgBroker = null,
237 | rTask = null;
238 |
239 | bgBroker = rbroker.backgroundTaskBroker(brokerConfig)
240 | .complete(function(rTask, rTaskResult) {
241 | jobID = rTaskResult.id;
242 |
243 | t.notEqual(rTaskResult, null, 'rTaskResult');
244 | t.ok(rTaskResult.success, 'rTaskResult.success');
245 | t.equal(rTask.token, executionToken, 'execution-token');
246 | t.notEqual(jobID, null, 'jobID');
247 |
248 | fixtures.verifyJobExitStatus(bgBroker.owner(), jobID, 'Completed')
249 | .then(function() {
250 | t.pass('jobCompleted');
251 | }, function(err) {
252 | t.fail('jobCompleted: ' + err);
253 | })
254 | .ensure(function() {
255 | teardown(t, bgBroker, [jobID]);
256 | });
257 | })
258 | .error(function(err) {
259 | if (err.task) {
260 | t.fail('bgBroker.submit(rTask) failed: ' + err.get('error'));
261 | }
262 | });
263 |
264 | rTask = rbroker.backgroundTask({
265 | name: executionToken,
266 | descr: 'Background Task',
267 | rscriptname: 'Histogram of Auto Sales',
268 | rscriptdirectory: 'root',
269 | rscriptauthor: 'testuser'
270 | }, true);
271 |
272 | rTask.token = executionToken;
273 |
274 | bgBroker.submit(rTask);
275 | });
276 |
277 | /**
278 | * Test multiple "good" RTask executions distributed across the standard and
279 | * priority task queues.
280 | */
281 | test('should execute multiple "good" RTask executions distributed across standard and priority task queues', function(t) {
282 | var brokerConfig = {
283 | host: fixtures.endpoint,
284 | credentials: fixtures.credentials,
285 | logging: false
286 | },
287 | executionToken = 'testMultipleTaskExecutionWithMixedPriority',
288 | jobID = null,
289 | bgBroker = null,
290 | rTasks = [],
291 | priorityTaskTokens = [],
292 | standardTaskTokens = [],
293 | rTaskResults = [],
294 | jobResults = [],
295 | priorityTask = true,
296 | multipleTaskTestSize = 10;
297 |
298 | t.plan(4 + (multipleTaskTestSize * 2));
299 |
300 | bgBroker = rbroker.backgroundTaskBroker(brokerConfig)
301 | .complete(function(rTask, rTaskResult) {
302 | rTaskResults.push(rTaskResult);
303 | })
304 | .error(function(err, rTask) {
305 | if (rTask) {
306 | t.fail('bgBroker.submit(rTask) failed: ' + err.get('error'));
307 | }
308 | })
309 | .idle(function() {
310 | var promises = [],
311 | jobIDs = [];
312 |
313 | rTaskResults.forEach(function(result, i) {
314 | var jobID = result.id;
315 | jobIDs.push(result.id);
316 | promises.push(fixtures.verifyJobExitStatus(bgBroker.owner(),
317 | jobID, 'Completed'));
318 | });
319 |
320 | when.all(promises).then(function(results) {
321 | jobResults = jobResults.concat(results);
322 | })
323 | .ensure(function() {
324 | t.equal(rTasks.length, multipleTaskTestSize,
325 | 'rTasks and multipleTaskTestSize are the same size');
326 | t.equal(standardTaskTokens.length + priorityTaskTokens.length,
327 | multipleTaskTestSize, 'standardTaskTokens and ' +
328 | 'priorityTaskTokens are the same size');
329 | t.equal(rTaskResults.length, multipleTaskTestSize,
330 | 'rTaskResults and multipleTaskTestSize are the same size');
331 | rTaskResults.forEach(function(result) {
332 | t.ok(result.success, 'rTask ran to success');
333 | });
334 | jobResults.forEach(function(result) {
335 | t.ok(result.status, 'job exit status success');
336 | });
337 |
338 | teardown(t, bgBroker, jobIDs);
339 | });
340 | }); // ideal
341 |
342 | for (var i = 0; i < multipleTaskTestSize; i++) {
343 | var rTask = rbroker.backgroundTask({
344 | name: executionToken,
345 | descr: 'Background Task',
346 | rscriptname: 'Histogram of Auto Sales',
347 | rscriptdirectory: 'root',
348 | rscriptauthor: 'testuser'
349 | }),
350 | rTaskToken = null;
351 |
352 | rTasks.push(rTask);
353 | rTaskToken = bgBroker.submit(rTask, priorityTask);
354 |
355 | if (priorityTask) {
356 | priorityTaskTokens.push(rTaskToken);
357 | } else {
358 | standardTaskTokens.push(rTaskToken);
359 | }
360 |
361 | // Flip priority flag to altenate task type.
362 | priorityTask = !priorityTask;
363 | }
364 | });
365 |
366 | /**
367 | * Test "bad" repository-managed script based RTask execution with an
368 | * `execution-token`.
369 | */
370 | test('should execute "bad" repository-managed script based RTask execution unsuccessfully with `execution-token`', function(t) {
371 | t.plan(4);
372 |
373 | var brokerConfig = {
374 | host: fixtures.endpoint,
375 | credentials: fixtures.credentials,
376 | logging: false
377 | },
378 | executionToken = 'testTaskExecutionWithTokenBadScript',
379 | jobID = null,
380 | bgBroker = null,
381 | rTask = null;
382 |
383 | bgBroker = rbroker.backgroundTaskBroker(brokerConfig)
384 | .complete(function(rTask, rTaskResult) {
385 | jobID = rTaskResult.id;
386 |
387 | t.notEqual(rTaskResult, null, 'rTaskResult');
388 | t.notEqual(jobID, null, 'jobID');
389 |
390 | fixtures.verifyJobExitStatus(bgBroker.owner(), jobID, 'Failed')
391 | .then(function(result) {
392 | t.ok(result.status, 'job should fail');
393 | })
394 | .ensure(function() {
395 | teardown(t, bgBroker, [jobID]);
396 | });
397 | })
398 | .error(function(err) {
399 | if (err.task) {
400 | t.fail('bgBroker.submit(rTask) failed: ' + err.get('error'));
401 | }
402 | });
403 |
404 | rTask = rbroker.backgroundTask({
405 | name: executionToken,
406 | descr: 'Background Task',
407 | rscriptname: 'This Script Does Not Exist',
408 | rscriptdirectory: 'root',
409 | rscriptauthor: 'testuser'
410 | });
411 |
412 | rTask.token = executionToken;
413 |
414 | bgBroker.submit(rTask);
415 | });
416 |
417 | /**
418 | * Test "bad" code-block based RTask execution with execution-token.
419 | */
420 | test('should execute "bad" code-block based RTask execution unsuccessfully with `execution-token`', function(t) {
421 | t.plan(4);
422 |
423 | var brokerConfig = {
424 | host: fixtures.endpoint,
425 | credentials: fixtures.credentials,
426 | logging: false
427 | },
428 | executionToken = 'testTaskExecutionWithTokenBadCode',
429 | jobID = null,
430 | bgBroker = null,
431 | rTask = null;
432 |
433 | bgBroker = rbroker.backgroundTaskBroker(brokerConfig)
434 | .complete(function(rTask, rTaskResult) {
435 | jobID = rTaskResult.id;
436 |
437 | t.notEqual(rTaskResult, null, 'rTaskResult');
438 | t.notEqual(jobID, null, 'jobID');
439 |
440 | fixtures.verifyJobExitStatus(bgBroker.owner(), jobID, 'Failed')
441 | .then(function(result) {
442 | t.ok(result.status, 'job should fail');
443 | })
444 | .ensure(function() {
445 | teardown(t, bgBroker, [jobID]);
446 | });
447 | })
448 | .error(function(err) {
449 | if (err.task) {
450 | t.fail('bgBroker.submit(rTask) failed: ' + err.get('error'));
451 | }
452 | });
453 |
454 | rTask = rbroker.backgroundTask({
455 | name: executionToken,
456 | descr: 'Background Task',
457 | code: 'x y' // invalid R code
458 | });
459 |
460 | rTask.token = executionToken;
461 |
462 | bgBroker.submit(rTask);
463 | });
464 |
465 | /**
466 | * Test RTask execution with "good" task execution options.
467 | */
468 | test('should successfully execute RTask with "good" task execution options', function(t) {
469 | t.plan(5);
470 |
471 | var jobID, bgBroker,
472 | brokerConfig = {
473 | host: fixtures.endpoint,
474 | credentials: fixtures.credentials,
475 | logging: false
476 | };
477 |
478 | bgBroker = rbroker.backgroundTaskBroker(brokerConfig)
479 | .complete(function(rTask, rTaskResult) {
480 | jobID = rTaskResult.id;
481 |
482 | t.notEqual(rTaskResult, null, 'rTaskResult');
483 | t.ok(rTaskResult.success, 'rTaskResult.success');
484 | t.notEqual(jobID, null, 'jobID');
485 |
486 | fixtures.verifyJobExitStatus(bgBroker.owner(), jobID, 'Completed')
487 | .then(function(result) {
488 | t.ok(result.status, 'job should complete');
489 | })
490 | .ensure(function() {
491 | teardown(t, bgBroker, [jobID]);
492 | });
493 | })
494 | .error(function(err) {
495 | if (err.task) {
496 | t.fail('bgBroker.submit(rTask) failed: ' + err.get('error'));
497 | }
498 | });
499 |
500 | bgBroker.submit(rbroker.backgroundTask(merge({
501 | name: 'testTaskExecutionWithGoodOptions',
502 | descr: 'Background Task',
503 | rscriptname: 'Histogram of Auto Sales',
504 | rscriptdirectory: 'root',
505 | rscriptauthor: 'testuser'
506 | }, fixtures.good())));
507 | });
508 |
509 | /**
510 | * Test RTask execution with "bad" task execution options.
511 | */
512 | test('should unsuccessfully execute RTask with "bad" task execution options', function(t) {
513 | t.plan(4);
514 |
515 | var bgBroker, jobID,
516 | brokerConfig = {
517 | host: fixtures.endpoint,
518 | credentials: fixtures.credentials,
519 | logging: false
520 | };
521 |
522 | bgBroker = rbroker.backgroundTaskBroker(brokerConfig)
523 | .complete(function(rTask, rTaskResult) {
524 | jobID = rTaskResult.id;
525 |
526 | t.notEqual(rTaskResult, null, 'rTaskResult');
527 | t.notEqual(jobID, null, 'jobID');
528 |
529 | fixtures.verifyJobExitStatus(bgBroker.owner(), jobID, 'Aborted')
530 | .then(function(result) {
531 | t.notOk(result.status, 'job should fail');
532 | })
533 | .ensure(function() {
534 | teardown(t, bgBroker, [jobID]);
535 | });
536 | })
537 | .error(function(err) {
538 | //
539 | // Suppress all errors other than 400: Project param not specified
540 | // on projectDelete call. All other errors should fail the test.
541 | //
542 | if (!err.task && err.get('errorCode') !== 400) {
543 | t.fail('bgBroker failed: ' + err.get('error'));
544 | }
545 | });
546 |
547 | bgBroker.submit(rbroker.backgroundTask(merge({
548 | name: 'testTaskExecutionWithBadOptions',
549 | descr: 'Background Task',
550 | rscriptname: 'Histogram of Auto Sales',
551 | rscriptdirectory: 'root',
552 | rscriptauthor: 'testuser'
553 | }, fixtures.bad())));
554 | });
555 |
556 | /**
557 | * Cancel long running RTask.
558 | */
559 | test('should successfully cancel long running RTask', function(t) {
560 | t.plan(5);
561 |
562 | var bgBroker, rTaskToken,
563 | brokerConfig = {
564 | host: fixtures.endpoint,
565 | credentials: fixtures.credentials
566 | };
567 |
568 | bgBroker = rbroker.backgroundTaskBroker(brokerConfig)
569 | .complete(function(rTask, rTaskResult) {
570 | jobID = rTaskResult.id;
571 |
572 | t.notEqual(rTaskResult, null, 'rTaskResult');
573 | t.ok(rTaskResult.success, 'rTaskResult.success');
574 | t.notEqual(jobID, null, 'jobID');
575 |
576 | rTaskToken.cancel(true);
577 |
578 | // wait 5 seconds for /r/job/cancel to take place
579 | setTimeout(function() {
580 | fixtures.verifyJobExitStatus(bgBroker.owner(), jobID, 'Cancelled')
581 | .then(function(result) {
582 | t.ok(result.status, 'jobCancelled');
583 | })
584 | .ensure(function() {
585 | bgBroker.shutdown().then(function() {
586 | t.pass('bgBroker.shutdown success');
587 | }, function(err) {
588 | t.fail('bgBroker.shutdown failed: ' + err.get('error'));
589 | });
590 | });
591 | }, 5000);
592 | })
593 | .error(function(err) {
594 | t.fail('bgBroker failed: ' + err.get('error'));
595 | });
596 |
597 | rTaskToken = bgBroker.submit(rbroker.backgroundTask({
598 | name: 'testJobCancel',
599 | descr: 'Background Task',
600 | code: 'Sys.sleep(10)' // sleep 10 seconds so we can cancel it
601 | }));
602 | });
--------------------------------------------------------------------------------