batch-cluster
Advanced tools
Comparing version 3.0.0 to 3.1.0
@@ -17,4 +17,4 @@ "use strict"; | ||
while (_) try { | ||
if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [0, t.value]; | ||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [op[0] & 2, t.value]; | ||
switch (op[0]) { | ||
@@ -73,17 +73,17 @@ case 0: case 1: t = op; break; | ||
// forking or plumbing issues are not the task's fault, so retry: | ||
this.proc.on("error", function (err) { return _this.onError("proc", err); }); | ||
this.proc.on("error", function (err) { return _this.onError("proc.error", err); }); | ||
this.proc.on("close", function () { return _this.onExit(); }); | ||
this.proc.on("exit", function () { return _this.onExit(); }); | ||
this.proc.on("disconnect", function () { return _this.onExit(); }); | ||
this.proc.stdin.on("error", function (err) { return _this.onError("stdin", err); }); | ||
this.proc.stdin.on("error", function (err) { return _this.onError("stdin.error", err); }); | ||
this.proc.stdout.on("error", function (err) { return _this.onError("stdout.error", err); }); | ||
this.proc.stdout.on("data", function (d) { return _this.onData(d); }); | ||
this.proc.stderr.on("error", function (err) { return _this.onError("stderr", err); }); | ||
this.proc.stderr.on("error", function (err) { return _this.onError("stderr.error", err); }); | ||
this.proc.stderr.on("data", function (err) { | ||
return _this.onError("stderr.data", new Error(String(err).trim())); | ||
_this.onError("stderr.data", new Error(cleanError(err))); | ||
}); | ||
this.startupTask = new Task_1.Task(opts.versionCommand, function (ea) { return ea; }); | ||
// Prevent unhandled startup task rejections from killing node: | ||
this.startupTask.promise.catch(function () { | ||
// | ||
this.startupTask.promise.catch(function (err) { | ||
BatchCluster_1.logger().warn("BatchProcess startup task was rejected: " + err); | ||
}); | ||
@@ -248,4 +248,7 @@ this.execTask(this.startupTask); | ||
} | ||
error = new Error(source + ": " + _error.message); | ||
error.stack = _error.stack; | ||
error = new Error(source + ": " + cleanError(_error.message)); | ||
if (_error.stack) { | ||
// Error stacks, if set, will not be redefined from a rethrow: | ||
error.stack = cleanError(_error.stack); | ||
} | ||
// clear the task before ending so the onExit from end() doesn't retry the task: | ||
@@ -282,3 +285,6 @@ this.clearCurrentTask(); | ||
if (this.running) { | ||
throw new Error("BatchProcess.onExit() called on a running process"); | ||
BatchCluster_1.logger().error("BatchProcess.onExit() called on a running process", { | ||
pid: this.pid, | ||
currentTask: map(this.currentTask, function (ea) { return ea.command; }) | ||
}); | ||
} | ||
@@ -303,3 +309,3 @@ this._ended = true; | ||
if (fail != null) { | ||
var err = new Error(fail[1].trim() || "command error"); | ||
var err = new Error(cleanError(fail[1]) || "command error"); | ||
this.onError("onData", err, true, this.currentTask); | ||
@@ -334,2 +340,14 @@ } | ||
exports.BatchProcess = BatchProcess; | ||
function map(obj, f) { | ||
return obj != null ? f(obj) : undefined; | ||
} | ||
/** | ||
* When we wrap errors, an Error always prefixes the toString() and stack with | ||
* "Error: ", so we can remove that prefix. | ||
*/ | ||
function cleanError(s) { | ||
return String(s) | ||
.trim() | ||
.replace(/^error:? ?/gi, ""); | ||
} | ||
function ensureSuffix(s, suffix) { | ||
@@ -336,0 +354,0 @@ return s.endsWith(suffix) ? s : s + suffix; |
@@ -17,4 +17,4 @@ "use strict"; | ||
while (_) try { | ||
if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [0, t.value]; | ||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [op[0] & 2, t.value]; | ||
switch (op[0]) { | ||
@@ -21,0 +21,0 @@ case 0: case 1: t = op; break; |
@@ -12,5 +12,7 @@ export declare type Log = (message: string, ...optionalParams: any[]) => void; | ||
} | ||
export declare const LogLevels: (keyof Logger)[]; | ||
/** | ||
* Default `Logger` implementation. `debug` and `info` go to | ||
* util.debuglog("batch-cluster")`. `warn` and `error` go to `console`. | ||
* util.debuglog("batch-cluster")`. `warn` and `error` go to `console.warn` and | ||
* `console.error`. | ||
*/ | ||
@@ -24,1 +26,4 @@ export declare const ConsoleLogger: Logger; | ||
export declare function logger(): Logger; | ||
export declare function withLevels(delegate: Logger): Logger; | ||
export declare function withTimestamps(delegate: Logger): Logger; | ||
export declare function filterLevels(logger: Logger, minLogLevel: keyof Logger): Logger; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var util_1 = require("util"); | ||
exports.LogLevels = [ | ||
"trace", | ||
"debug", | ||
"info", | ||
"warn", | ||
"error" | ||
]; | ||
var _debuglog = util_1.debuglog("batch-cluster"); | ||
var noop = function () { }; | ||
var noop = function () { return undefined; }; | ||
/** | ||
* Default `Logger` implementation. `debug` and `info` go to | ||
* util.debuglog("batch-cluster")`. `warn` and `error` go to `console`. | ||
* util.debuglog("batch-cluster")`. `warn` and `error` go to `console.warn` and | ||
* `console.error`. | ||
*/ | ||
@@ -46,4 +54,4 @@ exports.ConsoleLogger = Object.freeze({ | ||
function setLogger(l) { | ||
if ([l.debug, l.info, l.warn, l.error].some(function (f) { return typeof f !== "function"; })) { | ||
throw new Error("invalid logger"); | ||
if (exports.LogLevels.some(function (ea) { return typeof l[ea] !== "function"; })) { | ||
throw new Error("invalid logger, must implement " + exports.LogLevels); | ||
} | ||
@@ -57,2 +65,40 @@ _logger = l; | ||
exports.logger = logger; | ||
function withLevels(delegate) { | ||
var timestamped = {}; | ||
exports.LogLevels.forEach(function (ea) { | ||
var prefix = (ea + " ").substring(0, 5) + " | "; | ||
timestamped[ea] = function (message) { | ||
var optionalParams = []; | ||
for (var _i = 1; _i < arguments.length; _i++) { | ||
optionalParams[_i - 1] = arguments[_i]; | ||
} | ||
return message != null && delegate[ea].apply(delegate, [prefix + message].concat(optionalParams)); | ||
}; | ||
}); | ||
return timestamped; | ||
} | ||
exports.withLevels = withLevels; | ||
function withTimestamps(delegate) { | ||
var timestamped = {}; | ||
exports.LogLevels.forEach(function (ea) { | ||
return (timestamped[ea] = function (message) { | ||
var optionalParams = []; | ||
for (var _i = 1; _i < arguments.length; _i++) { | ||
optionalParams[_i - 1] = arguments[_i]; | ||
} | ||
return message != null && delegate[ea].apply(delegate, [new Date().toISOString() + " | " + message].concat(optionalParams)); | ||
}); | ||
}); | ||
return timestamped; | ||
} | ||
exports.withTimestamps = withTimestamps; | ||
function filterLevels(logger, minLogLevel) { | ||
var minLogLevelIndex = exports.LogLevels.indexOf(minLogLevel); | ||
var filtered = {}; | ||
exports.LogLevels.forEach(function (ea, idx) { | ||
return (filtered[ea] = idx < minLogLevelIndex ? noop : logger[ea].bind(logger)); | ||
}); | ||
return filtered; | ||
} | ||
exports.filterLevels = filterLevels; | ||
//# sourceMappingURL=Logger.js.map |
{ | ||
"name": "batch-cluster", | ||
"version": "3.0.0", | ||
"version": "3.1.0", | ||
"description": "Manage a cluster of child processes", | ||
@@ -31,7 +31,7 @@ "main": "dist/BatchCluster.js", | ||
"devDependencies": { | ||
"@types/chai": "^4.1.3", | ||
"@types/chai": "^4.1.4", | ||
"@types/chai-as-promised": "^7.1.0", | ||
"@types/chai-string": "^1.4.1", | ||
"@types/mocha": "^5.2.2", | ||
"@types/node": "^10.3.2", | ||
"@types/node": "^10.3.3", | ||
"chai": "^4.1.2", | ||
@@ -45,9 +45,9 @@ "chai-as-promised": "^7.1.1", | ||
"seedrandom": "^2.4.3", | ||
"serve": "^8.2.0", | ||
"serve": "^9.0.0", | ||
"source-map-support": "^0.5.6", | ||
"timekeeper": "^2.1.2", | ||
"typedoc": "^0.11.1", | ||
"typescript": "^2.9.1", | ||
"typescript": "^2.9.2", | ||
"wtfnode": "^0.7.0" | ||
} | ||
} |
144
README.md
@@ -42,26 +42,26 @@ # batch-cluster | ||
1. Create a singleton instance of | ||
[BatchCluster](/classes/_batchcluster_.batchcluster.html). | ||
1. Create a singleton instance of | ||
[BatchCluster](/classes/_batchcluster_.batchcluster.html). | ||
Note the [constructor | ||
options](/classes/_batchcluster_.batchcluster.html#constructor) takes a union | ||
type of | ||
Note the [constructor | ||
options](/classes/_batchcluster_.batchcluster.html#constructor) takes a union | ||
type of | ||
* [ChildProcessFactory](/interfaces/_batchcluster_.childprocessfactory.html) and | ||
* [BatchProcessOptions](/interfaces/_batchcluster_.batchprocessoptions.html), | ||
- [ChildProcessFactory](/interfaces/_batchcluster_.childprocessfactory.html) and | ||
- [BatchProcessOptions](/interfaces/_batchcluster_.batchprocessoptions.html), | ||
both of which have no defaults, and | ||
* [BatchClusterOptions](/classes/_batchcluster_.batchclusteroptions.html), | ||
- [BatchClusterOptions](/classes/_batchcluster_.batchclusteroptions.html), | ||
which has defaults that may or may not be relevant to your application. | ||
1. The [default](/modules/_logger_.consolelogger.html) logger writes warning and | ||
error messages to `console.warn` and `console.error`. You can change this to | ||
your logger by using [setLogger](/modules/_logger_.html#setlogger). | ||
1. The [default](/modules/_logger_.consolelogger.html) logger writes warning and | ||
error messages to `console.warn` and `console.error`. You can change this to | ||
your logger by using [setLogger](/modules/_logger_.html#setlogger). | ||
1. Implement the [Parser](/modules/_task_.html#parser) class to parse results from your child | ||
process. | ||
1. Implement the [Parser](/modules/_task_.html#parser) class to parse results from your child | ||
process. | ||
1. Construct a [Task](/classes/_task_.task.html) with the desired command and | ||
the parser you built in the previous step, and submit it to your BatchCluster | ||
singleton's | ||
[enqueueTask](/classes/_batchcluster_.batchcluster.html#enqueuetask) method. | ||
1. Construct a [Task](/classes/_task_.task.html) with the desired command and | ||
the parser you built in the previous step, and submit it to your BatchCluster | ||
singleton's | ||
[enqueueTask](/classes/_batchcluster_.batchcluster.html#enqueuetask) method. | ||
@@ -77,18 +77,26 @@ See | ||
* 💔 Non-backwards-compatible API changes | ||
- 💔 Non-backwards-compatible API changes | ||
### The `MINOR` or `UPDATE` version is incremented for | ||
* ✨ Backwards-compatible features | ||
- ✨ Backwards-compatible features | ||
### The `PATCH` version is incremented for | ||
* 🐞 Backwards-compatible bug fixes | ||
* 📦 Minor packaging changes | ||
- 🐞 Backwards-compatible bug fixes | ||
- 📦 Minor packaging changes | ||
## Changelog | ||
### v3.1.0 | ||
- ✨ Added simple timestamp and levels logger prefixer for tests | ||
- 🐞 Errors rethrown via BatchProcess now strip extraneous `Error:` prefixes | ||
- 🐞 For a couple internal errors (versionCommend startup errors and internal | ||
state inconsistencies on `onExit` that aren't fatal), we now log `.error` | ||
rather than throw Error() or ignore. | ||
### v3.0.0 | ||
* ✨/💔 **`Task` promises are only rejected with `Error` instances now.** Note | ||
- ✨/💔 **`Task` promises are only rejected with `Error` instances now.** Note | ||
that also means that `BatchProcessObserver` types are more strict. It could be | ||
@@ -103,5 +111,5 @@ argued that this isn't an API breaking change as it only makes rejection | ||
* 🐞 Windows taskkill `/PID` option seemed to work downcased, but the docs say | ||
- 🐞 Windows taskkill `/PID` option seemed to work downcased, but the docs say | ||
to use uppercase, so I've updated it. | ||
* 📦 Upgrade all deps including TypeScript to 2.9 | ||
- 📦 Upgrade all deps including TypeScript to 2.9 | ||
@@ -112,9 +120,9 @@ (v2.1.2 is the same contents, but `np` had a crashbug during publish) | ||
* 📦 More robust `end` for `BatchProcess`, which may prevent very long-lived | ||
- 📦 More robust `end` for `BatchProcess`, which may prevent very long-lived | ||
consumers from sporadically leaking child processes on Mac and linux. | ||
* 📦 Added Node 10 to the build matrix. | ||
- 📦 Added Node 10 to the build matrix. | ||
### v2.1.0 | ||
* 📦 Introduced `Logger.trace` and moved logging related to per-task items down | ||
- 📦 Introduced `Logger.trace` and moved logging related to per-task items down | ||
to `trace`, as heavy load and large request or response payloads could | ||
@@ -127,17 +135,17 @@ overwhelm loggers. If you really want to see on-the-wire requests and results, | ||
* 💔 Replaced `BatchClusterObserver` with a simple EventEmitter API on | ||
- 💔 Replaced `BatchClusterObserver` with a simple EventEmitter API on | ||
`BatchCluster` to be more idiomatic with node's API | ||
* 💔 v1.11.0 added "process reuse" after errors, but that turned out to be | ||
- 💔 v1.11.0 added "process reuse" after errors, but that turned out to be | ||
problematic in recovery, so that change was reverted (and with it, the | ||
`maxTaskErrorsPerProcess` parameter was removed) | ||
* ✨ `Rate` is simpler and more accurate now. | ||
- ✨ `Rate` is simpler and more accurate now. | ||
### v1.11.0 | ||
* ✨ Added new `BatchClusterObserver` for error and lifecycle monitoring | ||
* 📦 Added a number of additional logging calls | ||
- ✨ Added new `BatchClusterObserver` for error and lifecycle monitoring | ||
- 📦 Added a number of additional logging calls | ||
### v1.10.0 | ||
* 🐞 Explicitly use `timers.setInterval`. May address [this | ||
- 🐞 Explicitly use `timers.setInterval`. May address [this | ||
issue](https://stackoverflow.com/questions/48961238/electron-setinterval-implementation-difference-between-chrome-and-node). | ||
@@ -148,3 +156,3 @@ Thanks for the PR, [Tim Fish](https://github.com/timfish)! | ||
* 📦 Changed `BatchProcess.end()` to use `until()` rather than `Promise.race`, | ||
- 📦 Changed `BatchProcess.end()` to use `until()` rather than `Promise.race`, | ||
and always use `kill(pid, forced)` after waiting the shutdown grace period | ||
@@ -155,3 +163,3 @@ to prevent child process leaks. | ||
* ✨ New `Logger.setLogger()` for debug, info, warning, and errors. `debug` and | ||
- ✨ New `Logger.setLogger()` for debug, info, warning, and errors. `debug` and | ||
`info` defaults to Node's | ||
@@ -161,8 +169,8 @@ [debuglog](https://nodejs.org/api/util.html#util_util_debuglog_section), | ||
respectively. | ||
* 📦 docs generated by [typedoc](http://typedoc.org/) | ||
* 📦 Upgraded dependencies (including TypeScript 2.7, which has more strict | ||
- 📦 docs generated by [typedoc](http://typedoc.org/) | ||
- 📦 Upgraded dependencies (including TypeScript 2.7, which has more strict | ||
verifications) | ||
* 📦 Removed tslint, as `tsc` provides good lint coverage now | ||
* 📦 The code is now [prettier](https://github.com/prettier/prettier) | ||
* 🐞 `delay` now allows | ||
- 📦 Removed tslint, as `tsc` provides good lint coverage now | ||
- 📦 The code is now [prettier](https://github.com/prettier/prettier) | ||
- 🐞 `delay` now allows | ||
[unref](https://nodejs.org/api/timers.html#timers_timeout_unref)ing the | ||
@@ -174,5 +182,5 @@ timer, which, in certain circumstances, could prevent node processes from | ||
* ✨ onIdle now runs as many tasks as it can, rather than just one. This should | ||
- ✨ onIdle now runs as many tasks as it can, rather than just one. This should | ||
provide higher throughput. | ||
* 🐞 Removed stderr emit on race condition between onIdle and execTask. The | ||
- 🐞 Removed stderr emit on race condition between onIdle and execTask. The | ||
error condition was already handled appropriately--no need to console.error. | ||
@@ -182,34 +190,34 @@ | ||
* 📦 Exported `kill()` and `running()` from `BatchProcess` | ||
- 📦 Exported `kill()` and `running()` from `BatchProcess` | ||
### v1.6.1 | ||
* 📦 De-flaked some tests on mac, and added Node 8 to the build matrix. | ||
- 📦 De-flaked some tests on mac, and added Node 8 to the build matrix. | ||
### v1.6.0 | ||
* ✨ Processes are forcefully shut down with `taskkill` on windows and `kill -9` | ||
- ✨ Processes are forcefully shut down with `taskkill` on windows and `kill -9` | ||
on other unix-like platforms if they don't terminate after sending the | ||
`exitCommand`, closing `stdin`, and sending the proc a `SIGTERM`. Added a test | ||
harness to exercise. | ||
* 📦 Upgrade to TypeScript 2.6.1 | ||
* 🐞 `mocha` tests don't require the `--exit` hack anymore 🎉 | ||
- 📦 Upgrade to TypeScript 2.6.1 | ||
- 🐞 `mocha` tests don't require the `--exit` hack anymore 🎉 | ||
### v1.5.0 | ||
* ✨ `.running()` works correctly for PIDs with different owners now. | ||
* 📦 `yarn upgrade --latest` | ||
- ✨ `.running()` works correctly for PIDs with different owners now. | ||
- 📦 `yarn upgrade --latest` | ||
### v1.4.2 | ||
* 📦 Ran code through `prettier` and delinted | ||
* 📦 Massaged test assertions to pass through slower CI systems | ||
- 📦 Ran code through `prettier` and delinted | ||
- 📦 Massaged test assertions to pass through slower CI systems | ||
### v1.4.1 | ||
* 📦 Replaced an errant `console.log` with a call to `log`. | ||
- 📦 Replaced an errant `console.log` with a call to `log`. | ||
### v1.4.0 | ||
* 🐞 Discovered `maxProcs` wasn't always utilized by `onIdle`, which meant in | ||
- 🐞 Discovered `maxProcs` wasn't always utilized by `onIdle`, which meant in | ||
certain circumstances, only 1 child process would be servicing pending | ||
@@ -220,14 +228,14 @@ requests. Added breaking tests and fixed impl. | ||
* 📦 Added tests to verify that the `kill(0)` calls to verify the child | ||
- 📦 Added tests to verify that the `kill(0)` calls to verify the child | ||
processes are still running work across different node version and OSes | ||
* 📦 Removed unused methods in `BatchProcess` (whose API should not be accessed | ||
- 📦 Removed unused methods in `BatchProcess` (whose API should not be accessed | ||
directly by consumers, so the major version remains at 1) | ||
* 📦 Switched to yarn and upgraded dependencies | ||
- 📦 Switched to yarn and upgraded dependencies | ||
### v1.2.0 | ||
* ✨ Added a configurable cleanup signal to ensure child processes shut down on | ||
- ✨ Added a configurable cleanup signal to ensure child processes shut down on | ||
`.end()` | ||
* 📦 Moved child process management from `BatchCluster` to `BatchProcess` | ||
* ✨ More test coverage around batch process concurrency, reuse, flaky task | ||
- 📦 Moved child process management from `BatchCluster` to `BatchProcess` | ||
- ✨ More test coverage around batch process concurrency, reuse, flaky task | ||
retries, and proper process shutdown | ||
@@ -237,3 +245,3 @@ | ||
* ✨ `BatchCluster` now has a force-shutdown `exit` handler to accompany the | ||
- ✨ `BatchCluster` now has a force-shutdown `exit` handler to accompany the | ||
graceful-shutdown `beforeExit` handler. For reference, from the | ||
@@ -245,13 +253,13 @@ [Node docs](https://nodejs.org/api/process.html#process_event_beforeexit): | ||
* ✨ Remove `Rate`'s time decay in the interests of simplicity | ||
- ✨ Remove `Rate`'s time decay in the interests of simplicity | ||
### v1.0.0 | ||
* ✨ Integration tests now throw deterministically random errors to simulate | ||
- ✨ Integration tests now throw deterministically random errors to simulate | ||
flaky child procs, and ensure retries and disaster recovery work as expected. | ||
* ✨ If the `processFactory` or `versionCommand` fails more often than a given | ||
- ✨ If the `processFactory` or `versionCommand` fails more often than a given | ||
rate, `BatchCluster` will shut down and raise exceptions to subsequent | ||
`enqueueTask` callers, rather than try forever to spin up processes that are | ||
most likely misconfigured. | ||
* ✨ Given the proliferation of construction options, those options are now | ||
- ✨ Given the proliferation of construction options, those options are now | ||
sanity-checked at construction time, and an error will be raised whose message | ||
@@ -262,11 +270,11 @@ contains all incorrect option values. | ||
* ✨ Added support and explicit tests for | ||
- ✨ Added support and explicit tests for | ||
[CR LF, CR, and LF](https://en.wikipedia.org/wiki/Newline) encoded streams | ||
from spawned processes | ||
* ✨ child processes are ended after `maxProcAgeMillis`, and restarted as needed | ||
* 🐞 `BatchCluster` now practices good listener hygene for `process.beforeExit` | ||
- ✨ child processes are ended after `maxProcAgeMillis`, and restarted as needed | ||
- 🐞 `BatchCluster` now practices good listener hygene for `process.beforeExit` | ||
### v0.0.1 | ||
* ✨ Extracted implementation and tests from | ||
- ✨ Extracted implementation and tests from | ||
[exiftool-vendored](https://github.com/mceachen/exiftool-vendored.js) |
Sorry, the diff of this file is not supported yet
150819
1661
267