Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: substack/zygote
base: 39e73c4866
...
head fork: substack/zygote
compare: 188c03685f
Checking mergeability… Don't worry, you can still create the pull request.
  • 5 commits
  • 14 files changed
  • 0 commit comments
  • 1 contributor
View
4 .travis.yml
@@ -0,0 +1,4 @@
+language: node_js
+node_js:
+ - 0.4
+ - 0.6
View
19 README.markdown
@@ -4,6 +4,8 @@ zygote
[Cellular-differentiation](http://en.wikipedia.org/wiki/Cellular_differentiation)
for worker drones in [seaport](https://github.com/substack/seaport) clusters.
+[![build status](https://secure.travis-ci.org/substack/zygote.png)](http://travis-ci.org/substack/zygote)
+
example
=======
@@ -46,3 +48,20 @@ zygote --seaport=localhost:7000 push cluster.json
Now there are 2 web, 1 auth, and 3 encoder services running across all your
zygote drones!
+
+Modify the cluster plan and run `zygote push` again to update what all the
+zygote drones are running.
+
+install
+=======
+
+With [npm](http://npmjs.org) do:
+
+```
+npm install zygote
+```
+
+license
+=======
+
+MIT
View
0  bin/cmd.js 100644 → 100755
File mode changed
View
11 example/z/auth.js
@@ -0,0 +1,11 @@
+var http = require('http');
+var seaport = require('seaport');
+var ports = seaport.connect(process.argv[2]);
+
+var server = http.createServer(function (req, res) {
+ res.end('auth beep boop');
+});
+
+ports.service('auth', function (port, ready) {
+ server.listen(port, ready);
+});
View
14 example/z/cluster.json
@@ -0,0 +1,14 @@
+{
+ "web" : {
+ "number" : 2,
+ "command" : "node web.js"
+ },
+ "auth" : {
+ "number" : 1,
+ "command" : "node auth.js"
+ },
+ "encoder" : {
+ "number" : 3,
+ "command" : "node encoder.js"
+ }
+}
View
3  example/z/config.json
@@ -0,0 +1,3 @@
+{
+ "seaport" : "localhost:7000"
+}
View
11 example/z/encoder.js
@@ -0,0 +1,11 @@
+var http = require('http');
+var seaport = require('seaport');
+var ports = seaport.connect(process.argv[2]);
+
+var server = http.createServer(function (req, res) {
+ res.end('encoder beep boop');
+});
+
+ports.service('encoder', function (port, ready) {
+ server.listen(port, ready);
+});
View
11 example/z/web.js
@@ -0,0 +1,11 @@
+var http = require('http');
+var seaport = require('seaport');
+var ports = seaport.connect(process.argv[2]);
+
+var server = http.createServer(function (req, res) {
+ res.end('web beep boop');
+});
+
+ports.service('web', function (port, ready) {
+ server.listen(port, ready);
+});
View
58 index.js
@@ -15,13 +15,17 @@ module.exports = function (ports) {
return {
drone : function (opts) {
if (!opts) opts = {};
- var service = drone(ports, opts.plan);
+ var service = drone(ports);
var meta = {
id : service.id,
capacity : opts.capacity || 100,
};
- return air(service).listen('zygote', { meta : meta });
+ var c = air(service).listen('zygote', { meta : meta });
+ c.on('close', function () {
+ service.close();
+ });
+ return c;
},
push : function (plan) {
ports.query('zygote', function (ps) {
@@ -42,14 +46,13 @@ module.exports = function (ports) {
});
});
- ports.close();
+ if (!isSeaport) ports.close();
});
},
};
};
-function drone (ports, plan) {
- if (!plan) prevPlan = {};
+function drone (ports) {
var procs = {};
var service = function (remote, conn) {
@@ -57,28 +60,38 @@ function drone (ports, plan) {
// todo: query zygote roles at the start and subscribe to update
var work = Object.keys(plan).reduce(function (acc, name) {
- acc[name] = plan[name].number;
+ var n = plan[name].number;
+ if (n) acc[name] = n;
return acc;
}, {});
var share = marx(workers, work)[id] || {};
- var names = Object.keys(prevPlan).concat(Object.keys(share));
+ var names = Object.keys(procs).concat(Object.keys(work));
names.forEach(function (name) {
- var diff = (share[name] || 0) - (prevPlan[name] || 0);
+ if (!procs[name]) procs[name] = [];
+
+ var diff = (share[name] || 0) - procs[name].length;
var cmd = plan[name].command;
+
if (!Array.isArray(cmd)) cmd = cmd.split(' ');
- for (var i = diff; i < 0; i++) {
+ for (var i = 0; i < -diff; i++) (function () {
// cull excess services
- procs[name]
+
+ var to = setTimeout(function () { ps.kill() }, 2000);
+ var ps = procs[name][procs[name].length - i - 1];
+
+ ps
.removeAllListeners('exit')
.on('exit', function () {
- delete procs[name];
+ clearTimeout(to);
+ var ix = procs[name].indexOf(this);
+ if (ix >= 0) procs[name].splice(ix, 1);
})
;
- procs[name].kill('SIGHUP');
- }
+ ps.kill('SIGHUP');
+ })(i);
function createProc (cmd) {
var ps = spawn(cmd[0], cmd.slice(1));
@@ -89,20 +102,29 @@ function drone (ports, plan) {
for (var i = 0; i < diff; i++) {
// spawn extra services
- procs[name] = createProc(cmd);
+ var ps = createProc(cmd);
+ procs[name].push(ps);
- procs[name].on('exit', function () {
+ ps.on('exit', function () {
+ var ps = this;
setTimeout(function () {
- procs[name] = createProc(cmd);
+ var ix = procs[name].indexOf(ps);
+ if (ix >= 0) procs[name][ix] = createProc(cmd);
}, 1000);
});
}
});
-
- prevShare = share;
};
};
var id = service.id = Math.random().toString(16).slice(2);
+ service.close = function () {
+ Object.keys(procs).forEach(function (name) {
+ procs[name].forEach(function (ps) {
+ ps.removeAllListeners('exit').kill();
+ });
+ });
+ };
+
return service;
}
View
38 package.json
@@ -0,0 +1,38 @@
+{
+ "name" : "zygote",
+ "description" : "cellular differentiation for seaport clusters",
+ "version" : "0.0.0",
+ "repository" : {
+ "type" : "git",
+ "url" : "git://github.com/substack/zygote.git"
+ },
+ "bin" : {
+ "zygote" : "bin/cmd.js"
+ },
+ "dependencies" : {
+ "seaport" : "~0.6.7",
+ "airport" : "~0.3.5",
+ "dnode" : "~0.9.10",
+ "marx" : "~0.0.1",
+ "optimist" : "~0.3.1"
+ },
+ "devDependencies" : {
+ "tap" : "~0.2.4"
+ },
+ "scripts" : {
+ "test" : "tap test/*.js"
+ },
+ "keywords" : [
+ "travis",
+ "ci"
+ ],
+ "engines" : {
+ "node" : ">=0.4.0"
+ },
+ "license" : "MIT",
+ "author" : {
+ "name" : "James Halliday",
+ "email" : "mail@substack.net",
+ "url" : "http://substack.net"
+ }
+}
View
65 test/z.js
@@ -0,0 +1,65 @@
+var test = require('tap').test;
+var spawn = require('child_process').spawn;
+var seaport = require('seaport');
+var zygote = require('../');
+
+process.chdir(__dirname + '/z');
+
+test('z', function (t) {
+ t.plan(3 * 4);
+ var ports = seaport.createServer();
+ var port = Math.floor(Math.random() * 5e4 + 1e4);
+ ports.listen(port);
+ var addr = 'localhost:' + port;
+
+ var drone0 = zygote(addr).drone();
+ var drone1 = zygote(addr).drone();
+
+ function sendPlan (plan, cb) {
+ zygote(addr).push(plan);
+ t.equal(ports.query('zygote').length, 2);
+
+ setTimeout(function () {
+ Object.keys(plan).forEach(function (name) {
+ t.equal(ports.query(name).length, plan[name].number);
+ });
+ cb();
+ }, 3000);
+ }
+
+ var plans = [
+ {
+ web : { number : 2, command : 'node web.js ' + addr },
+ auth : { number : 1, command : 'node auth.js ' + addr },
+ encoder : { number : 3, command : 'node encoder.js ' + addr },
+ },
+ {
+ web : { number : 3, command : 'node web.js ' + addr },
+ auth : { number : 4, command : 'node auth.js ' + addr },
+ encoder : { number : 1, command : 'node encoder.js ' + addr },
+ },
+ {
+ web : { number : 0, command : 'node web.js ' + addr },
+ auth : { number : 1, command : 'node auth.js ' + addr },
+ encoder : { number : 1, command : 'node encoder.js ' + addr },
+ },
+ ];
+
+ (function next () {
+ var plan = plans.shift();
+ setTimeout(function () {
+ sendPlan(plan, function () {
+ if (plans.length) next()
+ });
+ }, 1000);
+ })();
+
+ t.on('end', function () {
+ drone0.close();
+ drone1.close();
+ ports.close();
+ setTimeout(function () {
+ process.exit();
+ }, 100);
+ });
+});
View
11 test/z/auth.js
@@ -0,0 +1,11 @@
+var http = require('http');
+var seaport = require('seaport');
+var ports = seaport.connect(process.argv[2]);
+
+var server = http.createServer(function (req, res) {
+ res.end('auth beep boop');
+});
+
+ports.service('auth', function (port, ready) {
+ server.listen(port, ready);
+});
View
11 test/z/encoder.js
@@ -0,0 +1,11 @@
+var http = require('http');
+var seaport = require('seaport');
+var ports = seaport.connect(process.argv[2]);
+
+var server = http.createServer(function (req, res) {
+ res.end('encoder beep boop');
+});
+
+ports.service('encoder', function (port, ready) {
+ server.listen(port, ready);
+});
View
11 test/z/web.js
@@ -0,0 +1,11 @@
+var http = require('http');
+var seaport = require('seaport');
+var ports = seaport.connect(process.argv[2]);
+
+var server = http.createServer(function (req, res) {
+ res.end('web beep boop');
+});
+
+ports.service('web', function (port, ready) {
+ server.listen(port, ready);
+});

No commit comments for this range

Something went wrong with that request. Please try again.