UNPKG

@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
"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