See Semver.
MAJOR or API version is incremented forMINOR or UPDATE version is incremented forPATCH version is incremented forπ Backwards-compatible bug fixes
π¦ Minor packaging changes
isRetirementRequest set, or shouldIgnoreStderrLine for stderr, a task now receives the complete lines from one stdout or stderr chunk in a single onStdout() or onStderr() call, instead of one call per line. Each call rescans the task's accumulated output, so a task that printed 100,000 lines now takes about 55 ms instead of 30 seconds. taskData events and a task's stderr warning logs now cover these batches, as they cover chunks without these options. With streamFlushMillis: 0, output after a completion token in the same chunk now reaches the parser, including an unterminated fragment flushed before parsing; at zero delay that fragment previously ended the worker with stdout.error.isRetirementRequest was unset; stderr chunks were decoded separately when both isRetirementRequest and shouldIgnoreStderrLine were unset. ExifTool's -listx output contained ΠΠ°Π½Π°οΏ½οΏ½ΠΎΠ² where it printed ΠΠ°Π½Π°Π»ΠΎΠ².Task.onStdout(), Task.onStderr(), taskData, and noTaskData now always receive decoded strings, never Buffers. Listeners or subclasses that need a Buffer must convert with Buffer.from(data); including those strings in a Buffer.concat([...]) call throws a TypeError.FAIL\nPASS\n now fails even when delivered in one chunk. With pass: "OK" and fail: "NOT OK", NOT OK now fails instead of matching the overlapping pass token first. Later stdout is retained for parsing without changing that decision; stderr failure tokens still take precedence. Stdout matching and token removal no longer advance shared RegExp.lastIndex values.maxProcAgeMillis may now be less than taskTimeoutMillis. A child that reaches maxProcAgeMillis during a task already finished that task before being recycled, so the check rejected configurations that work.BatchCluster constructor now rejects a maxProcAgeMillis below spawnTimeoutMillis when taskTimeoutMillis is 0. Previously it skipped that check whenever task timeouts were disabled.Task.onStderr() now logs a task's stderr at warn, instead of the stream handler logging it before the task sees it. A Task subclass that removes lines it understands before calling super.onStderr() keeps those lines out of the log. These log lines now name the task (Task.toString()) instead of the process. Stderr that arrives with no pending task is still logged with the process name.TaskTimeoutError even when a taskTimeout listener ends its process. Previously the listener's end() made the task reject later with a generic "Process terminated before task completed" error, and childEnd recorded the listener's reason instead of "timeout".Task.resetTimeout(), so a worker that reports progress can restart its task's taskTimeoutMillis while the task advances. Timeouts now reject with the exported TaskTimeoutError.BatchProcess.end() now resolves only after the child exits, and rejects if the child is still running 5 seconds after termination. context.end() in taskData listeners returns the same promise. BatchCluster.end() rejects, and doesn't emit end, while a child it spawned is still running. A child keeps its maxProcs slot until it exits.isRetirementRequest to consume worker retirement control lines and recycle the worker after its current task settles. Recognized lines are excluded from task output, events, and stderr logging. Added BatchProcess.requestRetirement(), retirementRequested, and the expected termination reason "retired".shouldIgnoreStderrLine is enabled.streamFlushMillis so they cannot strand an idle worker or block queued tasks indefinitely.shouldIgnoreStderrLine to discard known benign stderr lines before logging or routing.This release closes several ways a child process could be leaked, and several ways a task's promise could be dropped. It is major because some fixes change runtime behavior for every consumer, even though no API was removed or renamed.
π BREAKING: outstanding work now keeps the event loop alive.
Everything this library owns is deliberately unref'd, so an idle cluster never stops a script from exiting. But unsettled work held nothing open either: when the loop drained, beforeExit fired and end() tore down a task the caller was still awaiting. A queued task's promise was abandoned entirely β node exited 0 with no error at all β and an assigned task was rejected with "Process terminated before task completed". The only thing that ever prevented this was the per-task timeout timer, which doesn't exist at the default taskTimeoutMillis of 0.
This is longstanding, not new in v19. An idle cluster still holds nothing, so the unrefStreams contract is unchanged. One consequence: if a child wedges and you haven't set taskTimeoutMillis, your process now stays alive rather than exiting silently. A visible hang beats losing work without a diagnostic, but it is a good reason to set that option.
π BREAKING: end() is a barrier. It previously drained the pool and resolved, which is not the same as "every child is gone": an in-flight processFactory() could hand back a live child afterwards, and a recycling process could still be seconds into its graceful shutdown. Either way, await bc.end(); process.exit(0) could orphan a child.
Explicit end() now waits for in-flight spawns and recycling, so it can take longer than it used to β and deliberately has no deadline, because until a factory returns we cannot know whether it already spawned a child. A factory that never settles keeps explicit shutdown pending.
Automatic cleanup on beforeExit stays bounded by spawnTimeoutMillis, since process exit can't wait forever on an opaque factory. When that bound expires, every child known to be alive is force-killed and any task still waiting on those terminations is rejected β unless you set cleanupChildProcs: false, which means you handle PID cleanup yourself.
π BREAKING: end() rejects tasks still queued, with BatchCluster.end() was called before this task could be assigned: <command>. Nothing settled them before, so calling end() with work outstanding now yields rejections where you previously got promises that never settled.
π BREAKING: maxFailedTasksPerProcess defaults to 0 (disabled), and works when you set it.
failedTaskCount was never incremented, so the previous default of 2 never recycled anything. Rather than silently enabling that rule for everyone, it is now off by default: a rejected task usually means bad input rather than a sick child, and it counts failures over the process's whole lifetime, so a long-lived child would be recycled after any two bad inputs, ever. If you set it explicitly, expect more churn and "broken" in childEndCounts. Prefer healthCheckCommand if you can ask the child directly.
π BREAKING: kill() returns false on EPERM instead of throwing, matching how it already handled ESRCH; unrecognized codes still throw. Callers signal lists of pids in loops, and one pid we aren't permitted to signal stranded every pid after it.
π BREAKING (TypeScript only): killProcessGroup is a required property of the exported InternalBatchProcessOptions and CombinedBatchProcessOptions types, so code building either by hand needs the new field. Passing options to the BatchCluster constructor is unaffected.
β¨ Added the killProcessGroup option (default false) and exported killGroup().
Enable it if your processFactory uses detached: true, which makes each child its own process group leader: shutdown then keeps ownership of that group even after the leader exits, so grandchildren are cleaned up too. Safe but pointless for non-detached children β the group signal simply fails and we signal the child directly. Windows has no POSIX process groups and ignores it.
π Fixed two outright child-process leaks. If new BatchProcess() threw after the factory returned a live child β a factory whose stdio omits stdin or stdout, or a childStart listener that throws β the child never entered the pool, leaving it invisible to pids(), recycling, end(), and the exit backstop, one orphan per spawn retry, surviving even the parent's exit. And a child spawned while end() was already draining joined a pool nothing would drain again.
π The exit backstop now tracks every child spawned and not yet seen to exit, rather than current pool membership, so an abrupt process.exit() during shutdown or recycling can't leave one behind. This supersedes the PID-snapshot workaround from v17.3.1.
π A consumer-supplied logger that throws can no longer strand a child: cleanup now runs before the log, and a termination that rejects for any reason force-kills the child before it is treated as finished.
π vacuumProcs() catches per-process end() rejections, as closeChildProcesses() always has. Its only caller discards the promise, so a throwing childEnd listener became an unhandled rejection β killing the host process and orphaning every child then mid-recycle.
π An IPC disconnect is no longer treated as an exit. It only means the channel closed, but it made running() false, which skipped both the SIGTERM and the SIGKILL. Children from an IPC-enabled factory (stdio: [..., "ipc"] or fork()) that disconnect while running are now terminated.
π¦ The published package no longer includes the spec suite's subprocess fixtures (*-helper.js). They require dist/test.js, which was already excluded, so they were broken weight in the tarball.
π BREAKING: Raised the minimum supported Node.js version to >=22. Node 20 reached end-of-life, so it is no longer supported or tested. Stay on v17 if you must run on Node 20.
π¦ Upgraded the build to TypeScript 6.0 and refreshed the dev toolchain. Adapted to three TS 6.0 behavior changes: moduleResolution switched from the deprecated "node" to "bundler" (the recommended path for module: commonjs), @types packages are now listed explicitly in compilerOptions.types since they are no longer auto-discovered, and @sinonjs/fake-timers now ships its own types (dropping the redundant @types/sinonjs__fake-timers). The published JavaScript and type declarations are unchanged.
π¦ Updated GitHub Actions workflow pins and the pinact installation step.
end(). When end() was called (either explicitly or via the beforeExit handler), the synchronous process.on("exit") SIGKILL backstop was removed before async cleanup completed. If the parent process exited before graceful termination finished, child processes survived as orphans. The fix snapshots live PIDs before cleanup begins and keeps the backstop registered until async teardown succeeds.findStreamFlushMillis() to auto-discover the minimum reliable streamFlushMillis value for a given child process on the current operating system and hardware. Uses a three-phase search (coarse binary search, validation, confirmation); callers should apply their own additive headroom for load spikes.β¨ Added exitCode and exitSignal properties to BatchProcess that are always populated when a child process exits. Access these in childEnd event handlers for complete diagnostic information about how processes terminated.
β¨ Added unexpectedExit getter to BatchProcess that returns true if the process exited due to a crash, error, or timeout (rather than proactive management like recycling). Use this to distinguish between expected non-zero exit codes and actual problems.
β¨ Exported ExpectedTerminationReasons constant listing reasons that indicate managed shutdowns ("worn", "old", "idle", etc.) vs unexpected exits.
β¨ Added cleanupChildProcsOnExit option (defaults to true) to control whether BatchCluster registers beforeExit and exit handlers for automatic child process cleanup.
Set to false if you want to manage process cleanup yourself, or if these handlers interfere with your application's exit behavior.
π Fixed the exit handler to synchronously kill child processes. The previous implementation called an async method, which doesn't work in exit handlers (only synchronous operations are allowed).
π BREAKING: Added unrefStreams option (defaults to true) that unreferences child process stdio streams.
Previously, even though proc.unref() was called on child processes, the stdio streams (stdin, stdout, stderr) kept the parent Node.js process alive, requiring explicit .end() calls.
With unrefStreams: true (the new default), scripts can now exit naturally without calling .end(). Child processes are cleaned up automatically when the parent exits.
To restore the previous behavior where the parent process stays alive until .end() is called:
new BatchCluster({ unrefStreams: false, ... });
Fixes exiftool-vendored#319.
π BREAKING: Removed maxReasonableProcessFailuresPerMinute option and fatal shutdown on spawn failures.
Spawn failures now log warnings and retry (rate-limited by minDelayBetweenSpawnMillis) instead of calling endCluster(). This fixes exiftool-vendored#312 where task timeouts were incorrectly triggering cluster shutdown.
Also removed: startErrorRatePerMinute from stats, endCluster callback from BatchClusterEventCoordinator.
π BREAKING: fatalError event was removed (as it is never broadcast anymore--see above!)
π BREAKING: Changed the default value of taskTimeoutMillis from 10 seconds to 0 (disabled).
There is no universally safe default for task timeouts--the appropriate value depends entirely on your application's workload. A 10-second default could cause legitimate long-running tasks (like, hypothetically, extraction of embedded metadata in videos) to fail unnecessarily.
If you rely on task timeouts, explicitly set taskTimeoutMillis to a value appropriate for your use case (2-10x longer than expected task duration under typical load).
π BREAKING: Removed unused Rate class
π TypeScript compilation now targets ES2022 with ESNext.Disposable. This shouldn't impact anyone, as we already require Node.js v20+.
π¦ Added Node.js v25 to the test matrix
π Fixed: stdin.write() errors now properly end the process instead of leaving a broken process in the pool
"This time, with feeling"
compile prerequisite was missed π€¦, so v15.0.0 has no code in it πͺΉ. v15.0.1 is better! It has code!π Deleted the standalone pids() function and associated code (including the ProcpsChecker). This function was exported but only used internally by tests. This fixes the issue #58 (by deleting the unused code! the best kind of bugfix). Thanks for the report, Zaczero!
π Dropped official support for Node v23, which is EOL.
π¦ Simplified prettier config to accept all defaults -- this added semicolons to every file.
π Dropped official support for Node v14, v16, and v18. Minimum Node.js version is now v20.
β¨ Added startup validation for procps availability: BatchCluster now throws ProcpsMissingError during construction if the required ps command (or tasklist on Windows) is not available. This provides clear, actionable error messages instead of cryptic runtime failures. Resolves #13 and #39.
π¦ Significant internal refactoring to improve maintainability:
ProcessPoolManager, TaskQueueManager, ProcessHealthMonitor, StreamHandler, ProcessTerminator)any with unknown throughout the codebaseπ Dropped official support for Node v16, which is EOL.
π Several methods, including BatchCluster#pids() were changed from async to sync (as they were needlessly async).
π¦ A number of timeout options can now be validly 0 to disable timeouts:
spawnTimeoutMillistaskTimeoutMillisπ¦ Added eslint @typescript-eslint/await-thenable rule and delinted.
π¦ Updated development dependencies and rebuilt docs
π pidExists now handles EPERM properly (previous implementation would
mischaracterize pids as being dead due to insufficient permissions)
π¦ Updated development dependencies and rebuilt docs
π/β¨ pidExists and killPid are no longer async, as process management
is now performed via node:process.kill(), instead of forking ps or tasklist.
π¦ Updated development dependencies and rebuilt docs
π Fix support for zero value of maxProcAgeMillis
π¦ Updated development dependencies and rebuilt docs
π Fix unref is not a function
π¦ Updated development dependencies and rebuilt docs
Rate measurement.β¨ If healthCheckCommand is set and any task fails, that child process will
have a health check run before being put back into rotation.
π¦ Updated development dependencies and rebuilt docs
π BatchCluster#maybeSpawnProcs in prior versions could spawn too many
processes, especially if process startup was slow. Heuristics for when to
spawn new processes now take into account pending task length and processes
busy due to initial setup.
π¦ BatchCluster.vacuumProcs returns a promise that is only fulfilled after
all reaped child processes have completed BatchProcess.#end.
π¦ BatchProcess.whyNotHealthy can now return startError.
π¦ childEnd is now emitted only after the child process exits
π¦ BatchCluster.#onIdle is debounced during the same event loop
π¦ Added startup and shutdown spec assertions
π¦ Updated development dependencies and rebuilt docs
π¦ Add Rate.msSinceLastEvent
π¦ Adjusted streamFlushMillis to remove onTaskData errors in CI.
β¨ Exported Rate. You might like it.
β¨ When child processes emit to stdout or stderr with no current task,
prior versions would emit an internalError. These are now given their own
new noTaskData events. Consumers may want to bump up streamFlushMillis if
they see this in production.
π/π¦ Increased defaults for streamFlushMillis, added tests to verify noTaskData events don't happen in CI.
π¦ Normalized node imports
β¨/π¦ Set minDelayBetweenSpawnMillis = 0 to fork child processes as soon as
they are needed (rather than waiting between spawn calls)
β¨/π¦ Set maxReasonableProcessFailuresPerMinute = 0 to disable process start
error rate detection.
β¨/π¦ New fatalError event emitted when
maxReasonableProcessFailuresPerMinute is exceeded and the instance shuts
itself down.
π¦ New simpler Rate implementation with better time decay handling
π¦ Several jsdoc improvements, including exporting WhyNotHeathy and
WhyNotReady
π Fixed issue
#15 by
restoring the call to #onIdleLater when tasks settle.
π Fixed issue with setMaxProcs which resulted in all idle processes being
reaped
π¦ The idle event was removed. You weren't using it, though, so I'm not
bumping major.
π¦ Process shutdown is handled more gracefully with new thenOrTimeout
(rather than the prior Promise.race call which resulted in a dangling
timeout)
π¦ Updated development dependencies and rebuilt docs
.end() and .closeChildProcesses() closes all child processes in parallelBatchProcess interface⨠Process state improvements
π Renamed event s/childExit/childEnd/
π childEnd and childStart events receive BatchProcess instances now
π Renamed healthy state s/dead/ended/
π¦ Made BatchProcess.whyNotHealthy persistent
π¦ Added several more WhyNotHealthy values
π¦ Perf: filterInPlace and count use for loops rather than closures
π¦ Added spec to verify .end rejects long-running pending tasks
π¦ Updated development dependencies and rebuilt docs
π/π¦ BatchProcess exposes a promise for the completion of the startup task,
which BatchCluster now uses to immediately run #onIdle and pop off any
pending work.
π¦ Updated development dependencies and rebuild docs
taskResolved on startup tasks.π The BatchProcessObserver signature was deleted, as BatchClusterEmitter is
now typesafe. Consumers should not have used this signature directly, but in
case anyone did, I bumped the major version.
β¨ Added BatchCluster.off to unregister event listeners provided to BatchCluster.on.
π¦ Private fields and methods now use the # private
prefix
rather than the TypeScript private modifier.
π¦ Minor tweaks (fixed several jsdoc errors, simplified some boolean logic, small reduction in promise chains, ...)
π¦ Updated development dependencies and rebuild docs
π¦ Added BatchCluster.procCount and BatchCluster.setMaxProcs, and new
BatchCluster.ChildEndCountType which includes a new tooMany value, which
is incremented when setMaxProcs is set to a smaller value.
π¦ Updated development dependencies
π/π¦ BatchProcess now end on spurious stderr/stdout, and reject tasks if ending.
π¦ Relaxed default for streamFlushMillis to deflake CI
π/π¦ RegExp pass/fail strings are escaped (which could conceivably be a breaking change, hence the major version bump)
π¦ Refactored stdout/stderr merging code and added more tests
π¦ Added new "taskResolved" event
π¦ Rebuild docs
π¦ Updated development dependencies
BatchProcessOptions fields⨠Added on("healthCheckError", err, proc) event
π Fixed process start lag (due to startup tasks not emitting an .onIdle)
π Reworked when health checks were run, and add tests to validate failing health checks recycle children
π¦ Rebuild docs
π Several fields were renamed to make things more consistent:
BatchCluster.pendingTasks was renamed to BatchCluster.pendingTaskCount.BatchCluster.pendingTasks method now matches BatchCluster.currentTasks, which both return Task[].BatchCluster.busyProcs was renamed to busyProcCount.BatchCluster.spawnedProcs was renamed to spawnedProcCount.β¨ Added support for "health checks" that run periodically on child processes.
Both healthCheckCommand and healthCheckIntervalMillis must be set to
enable this feature.
β¨ New pidCheckIntervalMillis to verify internal child process state is kept
in sync with the process table. Defaults to every 2 minutes. Will no-op if idle.
β¨ New BatchCluster.childEndCounts to report why child processes were recycled (currently "dead" | "ending" | "closed" | "worn" | "idle" | "broken" | "old" | "timeout" )
π¦ Cleaned up scheduling: the prior implementation generated a bunch of
Promises per idle period, which was rough on the GC. Use of Mutex is now
relegated to tests.
π¦ tsconfig now emits ES2018 output and doesn't have downlevelIteration,
which reduces the size of the generated javascript, but requires contemporary
versions of Node.js.
π¦ BatchClusterOptions doesn't mark fields as readonly anymore
π¦ Task has a default type of any now.
BatchCluster.currentTasksBatchCluster.closeChildProcesses() (ends child processes but doesn't .end() the BatchCluster instance)main branchNo new features in v6: just a breaking change so we can fix an old name collision that caused linting errors.
π Prior versions name-collided on Logger: both as an interface and as a
pseudonamespace for logger factories. This made eslint grumpy, and if anyone
actually used this bare-bones logger, it could have caused confusion.
Logger now references only the interface.
The builder functions are now named Log.
π¦ Updated development dependencies
Deferred.resolve now requires an argument (as per the new Promise spec).
As this is just a typing change (and Deferred is an internal
implementation), I'm not bumping the major version.BatchCluster can now be created with a Logger thunk.maxIdleMsPerProcess option: automatically shut down idle child
processes to reduce system resource consumption. Defaults to 0, which
disables this feature (and prevents me from having to increment the major
version!)BatchProcess's streams could cause an infinite loop on .end() when
stdout was destroyed.BatchProcess.ready now verifies the child process still existsError: onExit(exit) called end()BatchCluster.end() should return a Deferred<void>onStartError and onTaskError didn't get emitted.shutdown()stdin.write() with try/catch that rejects the current task and
closes the current process.stdin.end() with try/catch (as .writable isn't reliable)BatchProcess (which incurred GC
overhead even when disabled)BatchCluster.options. Note that the object is frozen at
construction.BatchProcess.end() didn't correctly implement gracefully (which
resulted in spurious end(): called while not idle errors), and allowed for
multiple calls to destroy and disconnect from the child process, which may or
may not have been ill-advised.BatchCluster.isIdle. Updated development dependencies. Deflaked CI by embiggeningBatchClusterOptions.cleanupChildProcs, in case you want to handle
process cleanup yourself.maxProcs is respected again. In prior builds, if tasks were enqueued all
at once, prior dispatch code would only spin 1 concurrent task at a time.BatchProcess.end would result in different promise
resolution targets: the second call to .end() would resolve before the
first. This was fixed.minDelayBetweenSpawnMillis was added, to help relieve undue system load on
startup. It defaults to 1.5 seconds and can be disabled by setting it to 0.Deferred's warn log messages..pass and .fail regex now support multiple line outputs per task.π BatchProcessOptions.pass
and .fail had poorly specified and implemented failure semantics. Prior
implementations would capture a "failed" string, but not tell the task that
the service returned a failure status.
Task Parsers already accept stdout and stderr, and are the "final word" in resolving or rejecting Tasks.
v5.2.0 provides a boolean to Parser's callable indicating if the wrapped
service returned pass or fail, and the Parser may return a Promise now, as
well.
There's a new SimpleParser implementation you can use that fails if stderr
is non-blank or a stream matched the .fail pattern.
π initial BatchProcess validation uses the new SimpleParser to verify the
initial versionCommand.
β¨ child process pids are delivered to event listeners on spawn and close. See BatchClusterEmitter.
π fix "Error: end() called when not idle" by debouncing stdout and stderr
readers. Note that this adds latency to every task. See
BatchProcessOptions's
streamFlushMillis option, which defaults to 10 milliseconds.
π RegExp for pass and fail tokens handle newline edge cases now.
π¦ re-added tslint and delinted code.
ChildProcessFactory supports thunks that return either a ChildProcess or
Promise<ChildProcess>rejectTaskOnStderr API, which was added in v4.1.0 and applied to all
tasks for a given BatchCluster instance, proved to be a poor decision, and
has been removed. The Parser API, which is task-specific, now receives
both stdin and stderr streams. Parsers then have the necessary context to
decide what to do on a per task or per task-type basis.taskData events with the data and the
current task (which may be undefined) as soon as the stream data is emitted.NoLogger.
Consumers may use the ConsoleLogger or another Logger implementation as
they see fit.stderr emissions:
BatchProcess.rejectTaskOnStderr is a per-task, per-error predicate which
allows for a given error to be handled without always rejecting the task. This
can be handy if the script you're wrapping (like ExifTool) writes non-fatal
warnings to stderr.BatchProcessOptions.pass and BatchProcessOptions.fail can be RegExp
instances now, if you have more exotic parsing needs.π Using Node 8+ to determine if a process is running with kill(pid, 0)
turns out to be unreliable (as it returns true even after the process exits).
I tried to pull in the best-maintained "process-exists" external dependency,
but that pulled in 15 more modules (this used to be a zero-deps module), and
it was extremely unperformant on Windows.
The TL;DR: is that running(pid) now returns a Promise<boolean>, which had
far-reaching signature changes to accomodate the new asynchronicity, hence the
major version bump.
π In an effort to reduce this library's complexity, I'm removing retry functionality. All parameters associated to retries are now gone.
β¨ Internal state validation is now exposed by BatchCluster, and is used by tests to ensure no internal errors happen during integration tests. Previously these errors were simply logged.
Logger methods, withLevels, withTimestamps, and filterLevels
were shoved into a new Logger namespace.Error: prefixesonExit that aren't fatal), we now log .error
rather than throw Error() or ignore.Task promises are only rejected with Error instances now. Note
that also means that BatchProcessObserver types are more strict. It could be
argued that this isn't an API breaking change as it only makes rejection
values more strict, but people may need to change their error handling, so I'm
bumping the major version to highlight that. Resolves
#3. Thanks for the
issue, Nils Knappmeier!/PID option seemed to work downcased, but the docs say
to use uppercase, so I've updated it.(v2.1.2 is the same contents, but np had a crashbug during publish)
end for BatchProcess, which may prevent very long-lived
consumers from sporadically leaking child processes on Mac and linux.Logger.trace and moved logging related to per-task items down
to trace, as heavy load and large request or response payloads could
overwhelm loggers. If you really want to see on-the-wire requests and results,
enable trace in your debugger implementation. By default, the
ConsoleLogger omits log messages with this level.BatchClusterObserver with a simple EventEmitter API on
BatchCluster to be more idiomatic with node's APImaxTaskErrorsPerProcess parameter was removed)Rate is simpler and more accurate now.BatchClusterObserver for error and lifecycle monitoringtimers.setInterval. May address this
issue.
Thanks for the PR, Tim Fish!BatchProcess.end() to use until() rather than Promise.race,
and always use kill(pid, forced) after waiting the shutdown grace period
to prevent child process leaks.Logger.setLogger() for debug, info, warning, and errors. debug and
info defaults to Node's
debuglog,
warn and error default to console.warn and console.error,
respectively.tsc provides good lint coverage nowdelay now allows
unrefing the
timer, which, in certain circumstances, could prevent node processes from
exiting gracefully until their timeouts expiredkill() and running() from BatchProcesstaskkill 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.mocha tests don't require the --exit hack anymore π.running() works correctly for PIDs with different owners now.yarn upgrade --latestprettier and delintedconsole.log with a call to log.maxProcs wasn't always utilized by onIdle, which meant in
certain circumstances, only 1 child process would be servicing pending
requests. Added breaking tests and fixed impl.kill(0) calls to verify the child
processes are still running work across different node version and OSesBatchProcess (whose API should not be accessed
directly by consumers, so the major version remains at 1).end()BatchCluster to BatchProcessBatchCluster now has a force-shutdown exit handler to accompany the
graceful-shutdown beforeExit handler. For reference, from the
Node docs:The 'beforeExit' event is not emitted for conditions causing explicit termination, such as calling process.exit() or uncaught exceptions.
Rate's time decay in the interests of simplicityprocessFactory 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.maxProcAgeMillis, and restarted as neededBatchCluster now practices good listener hygene for process.beforeExit