UNPKG

type-compiler

Version:

A TypeScript compiler plugin for enhanced runtime type checking and analysis with Zod validation

396 lines (337 loc) 13 kB
/** * Monitoring and debugging example for type-compiler's parallel processing * * This example demonstrates how to monitor worker activity, debug performance issues, * and optimize the parallel processing configuration in type-compiler. * * To run this example: * 1. Compile with: tsc --project tsconfig.json examples/monitoring.ts * 2. Run with: node examples/monitoring.js */ // ------------------------------------------------------------------------ // Example implementation of a monitoring interface for type-compiler // ------------------------------------------------------------------------ /** * Interface representing a worker activity event */ interface WorkerEvent { workerId: number; eventType: 'start' | 'complete' | 'error'; timestamp: number; taskId?: string; processingTime?: number; error?: string; typeComplexity?: number; } /** * Interface for worker performance metrics */ interface WorkerMetrics { workerId: number; tasksProcessed: number; totalProcessingTime: number; averageProcessingTime: number; errorCount: number; currentlyActive: boolean; } /** * Monitoring class that could be integrated with type-compiler * to track worker activity and performance */ class ParallelProcessingMonitor { private events: WorkerEvent[] = []; private workers: Map<number, WorkerMetrics> = new Map(); private startTime: number; private endTime: number | null = null; constructor(workerCount: number) { this.startTime = Date.now(); // Initialize worker metrics for (let i = 0; i < workerCount; i++) { this.workers.set(i, { workerId: i, tasksProcessed: 0, totalProcessingTime: 0, averageProcessingTime: 0, errorCount: 0, currentlyActive: false }); } } /** * Record a worker event */ recordEvent(event: WorkerEvent): void { this.events.push(event); const worker = this.workers.get(event.workerId); if (!worker) return; switch (event.eventType) { case 'start': worker.currentlyActive = true; break; case 'complete': worker.tasksProcessed++; worker.currentlyActive = false; if (event.processingTime) { worker.totalProcessingTime += event.processingTime; worker.averageProcessingTime = worker.totalProcessingTime / worker.tasksProcessed; } break; case 'error': worker.errorCount++; worker.currentlyActive = false; break; } } /** * Complete the monitoring session */ complete(): void { this.endTime = Date.now(); } /** * Get overall performance metrics */ getPerformanceReport(): any { const totalDuration = (this.endTime || Date.now()) - this.startTime; const totalTasks = Array.from(this.workers.values()).reduce((sum, w) => sum + w.tasksProcessed, 0); const totalErrors = Array.from(this.workers.values()).reduce((sum, w) => sum + w.errorCount, 0); const workerUtilization = Array.from(this.workers.values()).map(w => ({ workerId: w.workerId, tasksProcessed: w.tasksProcessed, percentOfTotal: (w.tasksProcessed / totalTasks * 100).toFixed(2) + '%', averageProcessingTime: w.averageProcessingTime.toFixed(2) + 'ms', errorRate: (w.errorCount / (w.tasksProcessed + w.errorCount) * 100).toFixed(2) + '%' })); return { overview: { duration: totalDuration + 'ms', totalTasks, tasksPerSecond: (totalTasks / (totalDuration / 1000)).toFixed(2), totalErrors, errorRate: (totalErrors / totalTasks * 100).toFixed(2) + '%' }, workerUtilization, recommendations: this.generateRecommendations(totalTasks, totalDuration, workerUtilization) }; } /** * Get a timeline of events */ getTimeline(): any[] { return this.events.map(event => ({ time: new Date(event.timestamp).toISOString(), workerId: event.workerId, event: event.eventType, taskId: event.taskId, processingTime: event.processingTime ? event.processingTime + 'ms' : undefined, error: event.error })); } /** * Generate optimization recommendations */ private generateRecommendations(totalTasks: number, duration: number, workerUtilization: any[]): string[] { const recommendations: string[] = []; // Check for idle workers const idleWorkers = workerUtilization.filter(w => w.tasksProcessed === 0); if (idleWorkers.length > 0) { recommendations.push(`${idleWorkers.length} workers were idle. Consider reducing workerCount.`); } // Check for uneven distribution const taskCounts = workerUtilization.map(w => parseInt(w.tasksProcessed)); const max = Math.max(...taskCounts); const min = Math.min(...taskCounts.filter(t => t > 0)); if (max > min * 2) { recommendations.push('Task distribution is uneven. Consider adjusting workerBatchSize for better balance.'); } // Check overall throughput const tasksPerSecond = totalTasks / (duration / 1000); if (tasksPerSecond < 10) { recommendations.push('Processing throughput is low. Consider profiling type processing logic for bottlenecks.'); } // Check if we need more workers const workerCount = this.workers.size; if (workerCount < 4 && tasksPerSecond < 20 && totalTasks > 100) { recommendations.push('Consider increasing workerCount to improve parallelism.'); } return recommendations.length > 0 ? recommendations : ['Current configuration appears optimal for the workload.']; } } // ------------------------------------------------------------------------ // Demonstration of monitoring with simulated worker activity // ------------------------------------------------------------------------ // Simulate a type compiler run with monitored parallel processing function simulateTypesProcessing(): void { // Configuration const workerCount = 4; const typeCount = 100; const simulatedDuration = 2000; // 2 seconds console.log(`Simulating processing ${typeCount} types with ${workerCount} workers...`); console.log(''); // Create monitor const monitor = new ParallelProcessingMonitor(workerCount); // Simulate worker events const startTime = Date.now(); const endTime = startTime + simulatedDuration; let currentTime = startTime; let taskId = 0; // Pre-generate all events to ensure time moves forward properly const events: WorkerEvent[] = []; // Helper to select an available worker const busyWorkers = new Set<number>(); const selectAvailableWorker = () => { const availableWorkers = Array.from(Array(workerCount).keys()) .filter(id => !busyWorkers.has(id)); if (availableWorkers.length === 0) return null; const workerId = availableWorkers[Math.floor(Math.random() * availableWorkers.length)]; busyWorkers.add(workerId); return workerId; }; // Helper to mark a worker as available const markWorkerAvailable = (workerId: number) => { busyWorkers.delete(workerId); }; // Generate all tasks and completions while (taskId < typeCount && currentTime < endTime) { const workerId = selectAvailableWorker(); if (workerId === null) { // All workers busy, advance time currentTime += 10; continue; } // Calculate a processing time between 10ms and 100ms const complexity = Math.floor(Math.random() * 10) + 1; const processingTime = (complexity * 10) * (0.8 + Math.random() * 0.4); // Add start event events.push({ workerId, eventType: 'start', timestamp: currentTime, taskId: `task_${taskId}`, typeComplexity: complexity }); // Add completion event (or error event with small probability) const isError = Math.random() < 0.05; // 5% error rate if (isError) { events.push({ workerId, eventType: 'error', timestamp: currentTime + processingTime, taskId: `task_${taskId}`, error: 'Simulated error in type processing', typeComplexity: complexity }); } else { events.push({ workerId, eventType: 'complete', timestamp: currentTime + processingTime, taskId: `task_${taskId}`, processingTime, typeComplexity: complexity }); } // Schedule worker to become available when task is complete setTimeout(() => markWorkerAvailable(workerId), processingTime); // Next task taskId++; // Move time forward randomly currentTime += Math.floor(Math.random() * 20) + 1; } // Sort events by timestamp events.sort((a, b) => a.timestamp - b.timestamp); // Process events in simulated real-time console.log('LIVE MONITORING:'); console.log('---------------'); // Process events let lastTimestamp = startTime; const processEvents = () => { const now = Date.now(); const simulatedNow = startTime + (now - startTime) * 10; // Speed up simulation 10x // Process all events up to simulated now const pendingEvents = events.filter(e => e.timestamp <= simulatedNow && e.timestamp >= lastTimestamp); for (const event of pendingEvents) { monitor.recordEvent(event); // Output live monitoring info const relativeTime = ((event.timestamp - startTime) / 1000).toFixed(2); const workerStatus = Array.from(monitor.getPerformanceReport().workerUtilization) .map((w: any) => `W${w.workerId}:${w.tasksProcessed}`).join(' '); console.log(`[${relativeTime}s] Worker ${event.workerId} ${event.eventType} ${event.taskId} ${workerStatus}`); } lastTimestamp = simulatedNow; // Continue if there are more events and we haven't reached the end time if (events.some(e => e.timestamp > lastTimestamp) && now - startTime < simulatedDuration / 10) { setTimeout(processEvents, 100); } else { // Complete monitoring monitor.complete(); // Display final report displayReport(monitor); } }; // Start processing events processEvents(); } // Display monitoring report function displayReport(monitor: ParallelProcessingMonitor): void { const report = monitor.getPerformanceReport(); console.log(''); console.log('PERFORMANCE REPORT:'); console.log('------------------'); console.log('Overall:'); console.log(`- Duration: ${report.overview.duration}`); console.log(`- Total tasks: ${report.overview.totalTasks}`); console.log(`- Tasks per second: ${report.overview.tasksPerSecond}`); console.log(`- Error rate: ${report.overview.errorRate}`); console.log(''); console.log('Worker Utilization:'); for (const worker of report.workerUtilization) { console.log(`- Worker ${worker.workerId}: ${worker.tasksProcessed} tasks (${worker.percentOfTotal}), avg ${worker.averageProcessingTime}/task`); } console.log(''); console.log('Recommendations:'); for (const recommendation of report.recommendations) { console.log(`- ${recommendation}`); } console.log(''); console.log('INTEGRATION WITH TYPE-COMPILER:'); console.log('-----------------------------'); console.log('To add this type of monitoring to your type-compiler project:'); console.log(''); console.log('1. Create a monitoring class similar to ParallelProcessingMonitor'); console.log('2. Instrument WorkerPool to record events:'); console.log(' - When sending a task to a worker'); console.log(' - When receiving a completed task'); console.log(' - When handling errors from workers'); console.log(''); console.log('3. Add monitoring output to your build process:'); console.log(''); console.log('```typescript'); console.log('// In your plugin configuration'); console.log('const monitor = new ParallelProcessingMonitor(options.workerCount);'); console.log(''); console.log('// In your worker pool'); console.log('processTypeWithWorker(type: ts.Type): string {'); console.log(' monitor.recordEvent({'); console.log(' workerId: this.currentWorkerId,'); console.log(' eventType: "start",'); console.log(' timestamp: Date.now(),'); console.log(' taskId: type.id?.toString()'); console.log(' });'); console.log(''); console.log(' // Process type...'); console.log(''); console.log(' monitor.recordEvent({'); console.log(' workerId: this.currentWorkerId,'); console.log(' eventType: "complete",'); console.log(' timestamp: Date.now(),'); console.log(' taskId: type.id?.toString(),'); console.log(' processingTime: Date.now() - startTime'); console.log(' });'); console.log('}'); console.log('```'); console.log(''); console.log('4. Log or visualize the monitoring data during the build process'); } // Run the simulation simulateTypesProcessing();