A fast, efficient Node.js Worker Thread Pool implementation
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Built-in performance metrics collection with detailed histogram statistics for runtime and wait times, including percentile calculations.
Main interface for accessing performance metrics from the pool.
/**
* Main histogram interface providing runtime and wait time statistics
*/
interface PiscinaHistogram {
/** Runtime statistics for task execution */
readonly runTime: PiscinaHistogramSummary;
/** Wait time statistics for task queuing */
readonly waitTime: PiscinaHistogramSummary;
/**
* Reset runtime histogram data
*/
resetRunTime(): void;
/**
* Reset wait time histogram data
*/
resetWaitTime(): void;
}Detailed statistical summary with percentile calculations.
/**
* Comprehensive statistical summary of timing data
* All values are in milliseconds
*/
interface PiscinaHistogramSummary {
/** Average/mean execution time in milliseconds */
readonly average: number;
/** Mean execution time in milliseconds (same as average) */
readonly mean: number;
/** Standard deviation of execution times */
readonly stddev: number;
/** Minimum recorded execution time */
readonly min: number;
/** Maximum recorded execution time */
readonly max: number;
// Percentile values (all in milliseconds)
/** 0.1st percentile (1 in 1000 tasks faster) */
readonly p0_001: number;
/** 1st percentile (1 in 100 tasks faster) */
readonly p0_01: number;
/** 10th percentile (1 in 10 tasks faster) */
readonly p0_1: number;
/** 1st percentile */
readonly p1: number;
/** 2.5th percentile */
readonly p2_5: number;
/** 10th percentile */
readonly p10: number;
/** 25th percentile (first quartile) */
readonly p25: number;
/** 50th percentile (median) */
readonly p50: number;
/** 75th percentile (third quartile) */
readonly p75: number;
/** 90th percentile */
readonly p90: number;
/** 97.5th percentile */
readonly p97_5: number;
/** 99th percentile */
readonly p99: number;
/** 99.9th percentile */
readonly p99_9: number;
/** 99.99th percentile */
readonly p99_99: number;
/** 99.999th percentile */
readonly p99_999: number;
}Access performance metrics through the main pool instance.
class Piscina {
/** Performance histogram (when recordTiming is enabled) */
readonly histogram: PiscinaHistogram;
/** Pool utilization as percentage (0-1) */
readonly utilization: number;
/** Pool runtime duration in milliseconds */
readonly duration: number;
/** Total number of completed tasks */
readonly completed: number;
}Usage Examples:
import Piscina from "piscina";
// Create pool with timing enabled (default)
const pool = new Piscina({
filename: "worker.js",
recordTiming: true, // Default: true
workerHistogram: true // Enable per-worker histograms
});
// Run some tasks
await Promise.all([
pool.run({ task: "fast" }),
pool.run({ task: "medium" }),
pool.run({ task: "slow" })
]);
// Access performance metrics
const stats = pool.histogram;
console.log('Runtime Statistics:');
console.log(`Average: ${stats.runTime.average.toFixed(2)}ms`);
console.log(`Median (p50): ${stats.runTime.p50.toFixed(2)}ms`);
console.log(`97.5th percentile: ${stats.runTime.p97_5.toFixed(2)}ms`);
console.log(`99th percentile: ${stats.runTime.p99.toFixed(2)}ms`);
console.log(`Min: ${stats.runTime.min.toFixed(2)}ms`);
console.log(`Max: ${stats.runTime.max.toFixed(2)}ms`);
console.log('\nWait Time Statistics:');
console.log(`Average wait: ${stats.waitTime.average.toFixed(2)}ms`);
console.log(`Median wait: ${stats.waitTime.p50.toFixed(2)}ms`);
console.log(`99th percentile wait: ${stats.waitTime.p99.toFixed(2)}ms`);
console.log('\nPool Statistics:');
console.log(`Utilization: ${(pool.utilization * 100).toFixed(2)}%`);
console.log(`Completed tasks: ${pool.completed}`);
console.log(`Running for: ${pool.duration.toFixed(2)}ms`);Individual worker performance tracking when workerHistogram is enabled.
interface PiscinaWorker {
/** Worker-specific histogram (when workerHistogram is enabled) */
readonly histogram: PiscinaHistogramSummary | null;
}Usage Examples:
import Piscina from "piscina";
const pool = new Piscina({
filename: "worker.js",
workerHistogram: true, // Enable per-worker tracking
maxThreads: 4
});
// Run tasks and monitor per-worker performance
await Promise.all(
Array.from({ length: 100 }, (_, i) => pool.run({ taskId: i }))
);
// Monitor individual worker performance
pool.threads.forEach((thread, index) => {
const workerStats = thread.histogram;
if (workerStats) {
console.log(`\nWorker ${index} Performance:`);
console.log(`Average runtime: ${workerStats.average.toFixed(2)}ms`);
console.log(`Median runtime: ${workerStats.p50.toFixed(2)}ms`);
console.log(`97.5th percentile: ${workerStats.p97_5.toFixed(2)}ms`);
}
});import Piscina from "piscina";
const pool = new Piscina({
filename: "worker.js",
recordTiming: true,
workerHistogram: true
});
// Real-time performance dashboard
function displayPerformanceMetrics() {
const stats = pool.histogram;
console.clear();
console.log('=== Piscina Performance Dashboard ===\n');
// Pool overview
console.log(`Pool Utilization: ${(pool.utilization * 100).toFixed(1)}%`);
console.log(`Active Threads: ${pool.threads.length}`);
console.log(`Queue Size: ${pool.queueSize}`);
console.log(`Completed Tasks: ${pool.completed}`);
console.log(`Pool Runtime: ${(pool.duration / 1000).toFixed(1)}s\n`);
// Runtime statistics
console.log('Runtime Statistics:');
console.log(` Mean: ${stats.runTime.mean.toFixed(2)}ms`);
console.log(` Median: ${stats.runTime.p50.toFixed(2)}ms`);
console.log(` P97.5: ${stats.runTime.p97_5.toFixed(2)}ms`);
console.log(` P99: ${stats.runTime.p99.toFixed(2)}ms\n`);
// Wait time statistics
console.log('Wait Time Statistics:');
console.log(` Mean: ${stats.waitTime.mean.toFixed(2)}ms`);
console.log(` Median: ${stats.waitTime.p50.toFixed(2)}ms`);
console.log(` P97.5: ${stats.waitTime.p97_5.toFixed(2)}ms`);
console.log(` P99: ${stats.waitTime.p99.toFixed(2)}ms\n`);
// Worker breakdown
console.log('Worker Performance:');
pool.threads.forEach((thread, index) => {
const usage = thread.currentUsage;
const hist = thread.histogram;
const avgTime = hist ? hist.average.toFixed(1) : 'N/A';
console.log(` Worker ${index}: ${usage} tasks, ${avgTime}ms avg`);
});
}
// Update dashboard every 2 seconds
const dashboardInterval = setInterval(displayPerformanceMetrics, 2000);
// Stop monitoring
setTimeout(() => {
clearInterval(dashboardInterval);
pool.close();
}, 60000);import Piscina from "piscina";
const pool = new Piscina({
filename: "worker.js",
recordTiming: true
});
// Performance thresholds
const THRESHOLDS = {
avgRuntimeMs: 1000, // Alert if average > 1s
p99RuntimeMs: 5000, // Alert if P99 > 5s
avgWaitTimeMs: 100, // Alert if wait > 100ms
utilizationPercent: 90 // Alert if utilization > 90%
};
function checkPerformanceAlerts() {
const stats = pool.histogram;
const utilization = pool.utilization * 100;
// Runtime alerts
if (stats.runTime.average > THRESHOLDS.avgRuntimeMs) {
console.warn(`⚠️ High average runtime: ${stats.runTime.average.toFixed(2)}ms`);
}
if (stats.runTime.p99 > THRESHOLDS.p99RuntimeMs) {
console.warn(`⚠️ High P99 runtime: ${stats.runTime.p99.toFixed(2)}ms`);
}
// Wait time alerts
if (stats.waitTime.average > THRESHOLDS.avgWaitTimeMs) {
console.warn(`⚠️ High average wait time: ${stats.waitTime.average.toFixed(2)}ms`);
}
// Utilization alerts
if (utilization > THRESHOLDS.utilizationPercent) {
console.warn(`⚠️ High utilization: ${utilization.toFixed(1)}%`);
}
// Queue backup alert
if (pool.queueSize > 10) {
console.warn(`⚠️ Queue backup: ${pool.queueSize} tasks waiting`);
}
}
// Check for alerts every 10 seconds
const alertInterval = setInterval(checkPerformanceAlerts, 10000);import Piscina from "piscina";
import { writeFileSync, appendFileSync } from "fs";
const pool = new Piscina({
filename: "worker.js",
recordTiming: true,
workerHistogram: true
});
// Log performance metrics to file
function logPerformanceMetrics() {
const timestamp = new Date().toISOString();
const stats = pool.histogram;
const logEntry = {
timestamp,
utilization: pool.utilization,
completed: pool.completed,
queueSize: pool.queueSize,
activeThreads: pool.threads.length,
runtime: {
mean: stats.runTime.mean,
median: stats.runTime.p50,
p97_5: stats.runTime.p97_5,
p99: stats.runTime.p99
},
waitTime: {
mean: stats.waitTime.mean,
median: stats.waitTime.p50,
p97_5: stats.waitTime.p97_5,
p99: stats.waitTime.p99
}
};
appendFileSync('piscina-performance.log', JSON.stringify(logEntry) + '\n');
}
// Log metrics every minute
const logInterval = setInterval(logPerformanceMetrics, 60000);
// Reset metrics periodically to avoid memory growth
setInterval(() => {
console.log('Resetting performance metrics...');
pool.histogram.resetRunTime();
pool.histogram.resetWaitTime();
}, 3600000); // Reset every hourReset performance data for fresh measurements.
interface PiscinaHistogram {
/**
* Reset all runtime measurement data
* Useful for periodic cleanup or benchmarking specific periods
*/
resetRunTime(): void;
/**
* Reset all wait time measurement data
* Useful for periodic cleanup or benchmarking specific periods
*/
resetWaitTime(): void;
}Usage Examples:
const pool = new Piscina({ filename: "worker.js" });
// Run baseline measurements
await runBaselineTasks();
// Reset for clean benchmark
pool.histogram.resetRunTime();
pool.histogram.resetWaitTime();
// Run benchmark
await runBenchmarkTasks();
// Get clean benchmark results
const benchmarkStats = pool.histogram;
console.log(`Benchmark average: ${benchmarkStats.runTime.average}ms`);Control performance monitoring behavior through pool options.
interface Options {
/** Enable/disable timing collection (default: true) */
recordTiming?: boolean;
/** Enable per-worker histograms (default: false) */
workerHistogram?: boolean;
}Usage Examples:
// Disable performance monitoring for maximum performance
const highPerformancePool = new Piscina({
filename: "worker.js",
recordTiming: false, // Disable metrics collection
workerHistogram: false
});
// Full monitoring for development/debugging
const debugPool = new Piscina({
filename: "worker.js",
recordTiming: true,
workerHistogram: true
});
// Production monitoring (pool-level only)
const productionPool = new Piscina({
filename: "worker.js",
recordTiming: true,
workerHistogram: false // Reduce overhead
});Install with Tessl CLI
npx tessl i tessl/npm-piscina