node/doc/topics/domain-resource-cleanup-example.js
Trevor Norris 4a74fc9776 doc: add domain postmortem
Do to various reasons, outlined in the committed document, domains were
only in core for 2 years before being deprecated. This outline explains
why they received criticism from the community and never gained traction
with module authors.

Also included is an example script that accompanies the postmortem
analysis.

PR-URL: https://github.com/nodejs/node/pull/6159
Reviewed-By: Robert Lindstädt <robert.lindstaedt@gmail.com>
Reviewed-By: Kelvin Knighton <keltheceo@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Brian White <mscdex@mscdex.net>
2016-04-15 15:12:26 -06:00

137 lines
3.3 KiB
JavaScript

'use strict';
const domain = require('domain');
const EE = require('events');
const fs = require('fs');
const net = require('net');
const util = require('util');
const print = process._rawDebug;
const pipeList = [];
const FILENAME = '/tmp/tmp.tmp';
const PIPENAME = '/tmp/node-domain-example-';
const FILESIZE = 1024;
var uid = 0;
// Setting up temporary resources
const buf = Buffer(FILESIZE);
for (var i = 0; i < buf.length; i++)
buf[i] = ((Math.random() * 1e3) % 78) + 48; // Basic ASCII
fs.writeFileSync(FILENAME, buf);
function ConnectionResource(c) {
EE.call(this);
this._connection = c;
this._alive = true;
this._domain = domain.create();
this._id = Math.random().toString(32).substr(2).substr(0, 8) + (++uid);
this._domain.add(c);
this._domain.on('error', () => {
this._alive = false;
});
}
util.inherits(ConnectionResource, EE);
ConnectionResource.prototype.end = function end(chunk) {
this._alive = false;
this._connection.end(chunk);
this.emit('end');
};
ConnectionResource.prototype.isAlive = function isAlive() {
return this._alive;
};
ConnectionResource.prototype.id = function id() {
return this._id;
};
ConnectionResource.prototype.write = function write(chunk) {
this.emit('data', chunk);
return this._connection.write(chunk);
};
// Example begin
net.createServer((c) => {
const cr = new ConnectionResource(c);
const d1 = domain.create();
fs.open(FILENAME, 'r', d1.intercept((fd) => {
streamInParts(fd, cr, 0);
}));
pipeData(cr);
c.on('close', () => cr.end());
}).listen(8080);
function streamInParts(fd, cr, pos) {
const d2 = domain.create();
var alive = true;
d2.on('error', (er) => {
print('d2 error:', er.message)
cr.end();
});
fs.read(fd, new Buffer(10), 0, 10, pos, d2.intercept((bRead, buf) => {
if (!cr.isAlive()) {
return fs.close(fd);
}
if (cr._connection.bytesWritten < FILESIZE) {
// Documentation says callback is optional, but doesn't mention that if
// the write fails an exception will be thrown.
const goodtogo = cr.write(buf);
if (goodtogo) {
setTimeout(() => streamInParts(fd, cr, pos + bRead), 1000);
} else {
cr._connection.once('drain', () => streamInParts(fd, cr, pos + bRead));
}
return;
}
cr.end(buf);
fs.close(fd);
}));
}
function pipeData(cr) {
const pname = PIPENAME + cr.id();
const ps = net.createServer();
const d3 = domain.create();
const connectionList = [];
d3.on('error', (er) => {
print('d3 error:', er.message);
cr.end();
});
d3.add(ps);
ps.on('connection', (conn) => {
connectionList.push(conn);
conn.on('data', () => {}); // don't care about incoming data.
conn.on('close', () => {
connectionList.splice(connectionList.indexOf(conn), 1);
});
});
cr.on('data', (chunk) => {
for (var i = 0; i < connectionList.length; i++) {
connectionList[i].write(chunk);
}
});
cr.on('end', () => {
for (var i = 0; i < connectionList.length; i++) {
connectionList[i].end();
}
ps.close();
});
pipeList.push(pname);
ps.listen(pname);
}
process.on('SIGINT', () => process.exit());
process.on('exit', () => {
try {
for (var i = 0; i < pipeList.length; i++) {
fs.unlinkSync(pipeList[i]);
}
fs.unlinkSync(FILENAME);
} catch (e) { }
});