@casoon/auditmysite
Version:
Professional website analysis suite with robust accessibility testing, Core Web Vitals performance monitoring, SEO analysis, and content optimization insights. Features isolated browser contexts, retry mechanisms, and comprehensive API endpoints for profe
284 lines • 10.2 kB
JavaScript
"use strict";
/**
* 🔧 Parallel Queue Adapter
*
* Adapter for parallel queue processing with worker management.
* Supports concurrent processing with configurable worker limits.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ParallelQueueAdapter = void 0;
const queue_adapter_1 = require("../queue-adapter");
const backpressure_controller_1 = require("../../backpressure-controller");
const resource_monitor_1 = require("../../resource-monitor");
class ParallelQueueAdapter extends queue_adapter_1.QueueAdapter {
constructor(config, callbacks) {
super(config, callbacks);
this.processingQueue = [];
this.workers = new Map();
this.isPaused = false;
this.initializeWorkers();
this.setupBackpressure();
this.setupResourceMonitoring();
}
/**
* Setup backpressure controller
*/
setupBackpressure() {
if (this.config.enableBackpressure) {
const backpressureConfig = {
enabled: true,
maxQueueSize: this.config.maxQueueSize || 1000,
backpressureThreshold: this.config.backpressureThreshold || 0.8,
maxMemoryUsageMB: this.config.maxMemoryUsage || 2048,
maxCpuUsagePercent: 85,
minDelayMs: 10,
maxDelayMs: 5000,
delayGrowthFactor: 1.5,
activationThreshold: 0.85,
deactivationThreshold: 0.65,
resourceSamplingIntervalMs: 2000,
maxErrorRatePercent: 15,
errorRateWindowSize: 20
};
this.backpressureController = new backpressure_controller_1.AdaptiveBackpressureController(backpressureConfig);
}
}
/**
* Setup resource monitoring
*/
setupResourceMonitoring() {
if (this.config.enableResourceMonitoring) {
const resourceConfig = {
enabled: true,
samplingIntervalMs: this.config.metricsCollectionInterval || 3000,
historySize: 100,
memoryWarningThresholdMB: Math.floor((this.config.maxMemoryUsage || 2048) * 0.75),
memoryCriticalThresholdMB: this.config.maxMemoryUsage || 2048,
cpuWarningThresholdPercent: 70,
cpuCriticalThresholdPercent: 85,
heapUsageWarningPercent: 75,
heapUsageCriticalPercent: 90,
eventLoopWarningDelayMs: 50,
eventLoopCriticalDelayMs: 100,
enableGCMonitoring: this.config.enableGarbageCollection || false,
gcWarningFrequency: 60,
disableInCI: true
};
this.resourceMonitor = new resource_monitor_1.ResourceMonitor(resourceConfig);
}
}
enqueue(data, options) {
const ids = [];
for (const item of data) {
const queueItem = this.createQueueItem(item, options?.priority);
this.processingQueue.push(queueItem);
ids.push(queueItem.id);
}
// Sort by priority (highest first) and timestamp (oldest first)
this.processingQueue.sort((a, b) => {
if (a.priority !== b.priority) {
return b.priority - a.priority;
}
return a.timestamp.getTime() - b.timestamp.getTime();
});
return ids;
}
async process(processor) {
if (this.isProcessing) {
throw new Error('Queue is already processing');
}
this.isProcessing = true;
this.startTime = new Date();
const completed = [];
const failed = [];
try {
// Start worker processes
const workerPromises = [];
for (const [workerId, worker] of this.workers) {
workerPromises.push(this.runWorker(workerId, processor, completed, failed));
}
// Wait for all workers to complete
await Promise.all(workerPromises);
// Queue empty
this.callbacks?.onQueueEmpty?.();
}
catch (error) {
this.callbacks?.onError?.(error instanceof Error ? error.message : String(error));
}
finally {
this.isProcessing = false;
this.endTime = new Date();
this.resetWorkers();
}
const statistics = this.getStatistics();
const duration = this.endTime && this.startTime
? this.endTime.getTime() - this.startTime.getTime()
: 0;
return {
completed,
failed,
statistics,
duration
};
}
getStatistics() {
const baseStats = this.getBaseStatistics();
// Add parallel queue specific stats
const activeWorkers = Array.from(this.workers.values()).filter(w => w.isActive).length;
return {
...baseStats,
pending: this.processingQueue.filter(item => item.status === 'pending').length,
processing: this.processingQueue.filter(item => item.status === 'processing').length,
activeWorkers
};
}
pause() {
this.isPaused = true;
}
resume() {
this.isPaused = false;
}
clear() {
this.processingQueue = [];
this.items.clear();
this.isPaused = false;
this.isProcessing = false;
this.startTime = undefined;
this.endTime = undefined;
this.resetWorkers();
}
configure(config) {
super.configure(config);
// Reinitialize workers if maxConcurrent changed
if (config.maxConcurrent && config.maxConcurrent !== this.workers.size) {
this.initializeWorkers();
}
}
/**
* Initialize worker pool
*/
initializeWorkers() {
this.workers.clear();
const workerCount = this.config.maxConcurrent || 3;
for (let i = 0; i < workerCount; i++) {
const workerId = `worker_${i}`;
this.workers.set(workerId, {
id: workerId,
isActive: false
});
}
}
/**
* Reset all workers
*/
resetWorkers() {
for (const worker of this.workers.values()) {
worker.isActive = false;
worker.currentItem = undefined;
worker.startTime = undefined;
}
}
/**
* Get next pending item from queue
*/
getNextItem() {
const index = this.processingQueue.findIndex(item => item.status === 'pending');
if (index === -1)
return null;
const item = this.processingQueue[index];
return item;
}
/**
* Run individual worker
*/
async runWorker(workerId, processor, completed, failed) {
const worker = this.workers.get(workerId);
while (!this.isPaused && (this.processingQueue.some(item => item.status === 'pending') ||
this.processingQueue.some(item => item.status === 'retrying'))) {
const item = this.getNextItem();
if (!item) {
// No items available, wait a bit
await this.delay(100);
continue;
}
// Assign item to worker
worker.isActive = true;
worker.currentItem = item;
worker.startTime = new Date();
// Update item status to processing
this.updateItemStatus(item.id, 'processing');
let attempts = 0;
let success = false;
while (attempts < item.maxAttempts && !success && !this.isPaused) {
attempts++;
item.attempts = attempts;
try {
// Process the item with timeout
const result = await Promise.race([
processor(item.data),
this.createTimeoutPromise()
]);
// Success
this.updateItemStatus(item.id, 'completed', { result });
completed.push(item);
success = true;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
if (attempts < item.maxAttempts) {
// Retry after delay
this.updateItemStatus(item.id, 'retrying', { error: errorMessage });
await this.delay(this.config.retryDelay || 1000);
}
else {
// Max attempts reached
this.updateItemStatus(item.id, 'failed', { error: errorMessage });
failed.push(item);
}
}
}
// Worker finished with item
worker.isActive = false;
worker.currentItem = undefined;
worker.startTime = undefined;
// Report progress
if (this.config.enableProgressReporting) {
this.callbacks?.onProgressUpdate?.(this.getStatistics());
}
}
}
/**
* Create timeout promise
*/
createTimeoutPromise() {
return new Promise((_, reject) => {
setTimeout(() => {
reject(new Error(`Processing timeout after ${this.config.timeout}ms`));
}, this.config.timeout || 10000);
});
}
/**
* Simple delay utility
*/
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* Get worker information
*/
getWorkerInfo() {
return Array.from(this.workers.values()).map(worker => ({
id: worker.id,
isActive: worker.isActive,
currentItem: worker.currentItem?.id
}));
}
/**
* Get active worker count
*/
getActiveWorkerCount() {
return Array.from(this.workers.values()).filter(w => w.isActive).length;
}
}
exports.ParallelQueueAdapter = ParallelQueueAdapter;
//# sourceMappingURL=parallel-queue-adapter.js.map