UNPKG

rezilient.js

Version:

Rezilient.js - Revolutionary offline-first framework with AI-awareness, principle-driven development, carbon-conscious computing, and self-healing capabilities

760 lines (646 loc) 20.8 kB
// src/scheduler/CarbonAwareScheduler.js import { BrowserAPICompat, EnvironmentDetector, NetworkCompat } from '../utils/environment.js'; /** * @class CarbonAwareScheduler * Intelligent scheduling system that optimizes operations based on carbon intensity, * network conditions, and user behavior patterns for sustainable computing * Universal compatibility (Node.js, Browser, React Native) */ export class CarbonAwareScheduler { constructor(options = {}) { this.options = { enableCarbonAwareness: options.enableCarbonAwareness !== false, carbonApiKey: options.carbonApiKey, carbonApiUrl: options.carbonApiUrl || 'https://api.carbonintensity.org.uk', maxDelayHours: options.maxDelayHours || 24, urgentThreshold: options.urgentThreshold || 5 * 60 * 1000, // 5 minutes batchSize: options.batchSize || 10, ...options }; // Task queue with priority levels this.taskQueue = { urgent: [], // Execute immediately regardless of carbon intensity high: [], // Execute within 1 hour normal: [], // Execute within 6 hours low: [], // Execute within 24 hours background: [] // Execute only during low carbon periods }; // Carbon intensity data this.carbonData = { current: { intensity: 'medium', forecast: [] }, lastUpdate: 0, updateInterval: 30 * 60 * 1000, // 30 minutes history: [] }; // Network and device conditions this.conditions = { network: 'unknown', battery: 'unknown', charging: false, connectionType: 'unknown' }; // Scheduling statistics this.stats = { tasksScheduled: 0, tasksExecuted: 0, carbonSaved: 0, energySaved: 0, averageDelay: 0 }; this.initialize(); } /** * Initialize the scheduler */ async initialize() { await this.updateCarbonData(); this.updateDeviceConditions(); this.setupPeriodicUpdates(); this.startScheduler(); } /** * Schedule a task with carbon awareness */ async scheduleTask(task, priority = 'normal', options = {}) { // Validate priority const validPriorities = ['urgent', 'high', 'normal', 'low', 'background']; if (!validPriorities.includes(priority)) { console.warn(`Invalid priority "${priority}", using "normal" instead`); priority = 'normal'; } const enhancedTask = { id: this.generateTaskId(), ...task, priority, scheduledAt: Date.now(), options: { maxDelay: options.maxDelay || this.getMaxDelayForPriority(priority), carbonAware: options.carbonAware !== false, networkAware: options.networkAware !== false, batteryAware: options.batteryAware !== false, ...options }, attempts: 0, lastAttempt: null, estimatedCarbonCost: this.estimateCarbonCost(task), estimatedDuration: options.estimatedDuration || 1000 }; // Add to appropriate queue this.taskQueue[priority].push(enhancedTask); this.stats.tasksScheduled++; // Trigger immediate execution for urgent tasks if (priority === 'urgent') { return this.executeTask(enhancedTask); } // Schedule execution based on optimal conditions this.scheduleOptimalExecution(enhancedTask); return enhancedTask.id; } /** * Schedule batch operations for optimal carbon efficiency */ async scheduleBatch(tasks, priority = 'normal', options = {}) { const batchId = this.generateTaskId(); const batchTask = { id: batchId, type: 'batch', tasks, priority, scheduledAt: Date.now(), options: { batchSize: options.batchSize || this.options.batchSize, ...options }, estimatedCarbonCost: tasks.reduce((total, task) => total + this.estimateCarbonCost(task), 0 ), estimatedDuration: tasks.reduce((total, task) => total + (task.estimatedDuration || 1000), 0 ) }; return this.scheduleTask(batchTask, priority, options); } /** * Update carbon intensity data from external APIs */ async updateCarbonData() { if (!this.options.enableCarbonAwareness) { this.carbonData.current.intensity = 'medium'; return; } try { // Try to get real carbon intensity data const carbonIntensity = await this.fetchCarbonIntensity(); if (carbonIntensity) { this.carbonData.current = carbonIntensity; this.carbonData.lastUpdate = Date.now(); // Add to history for trend analysis this.carbonData.history.push({ timestamp: Date.now(), intensity: carbonIntensity.intensity, value: carbonIntensity.value || 0 }); // Keep only last 24 hours of history const dayAgo = Date.now() - 24 * 60 * 60 * 1000; this.carbonData.history = this.carbonData.history.filter( entry => entry.timestamp > dayAgo ); } } catch (error) { console.warn('Failed to update carbon data, using fallback:', error); this.useFallbackCarbonData(); } } /** * Fetch carbon intensity from external API */ async fetchCarbonIntensity() { if (!this.options.carbonApiUrl) { return this.simulateCarbonIntensity(); } try { const response = await fetch(`${this.options.carbonApiUrl}/intensity`, { headers: this.options.carbonApiKey ? { 'Authorization': `Bearer ${this.options.carbonApiKey}` } : {} }); if (!response.ok) { throw new Error(`Carbon API error: ${response.status}`); } const data = await response.json(); return this.parseCarbonApiResponse(data); } catch (error) { console.warn('Carbon API request failed:', error); return this.simulateCarbonIntensity(); } } /** * Simulate carbon intensity based on time patterns */ simulateCarbonIntensity() { const hour = new Date().getHours(); const dayOfWeek = new Date().getDay(); // Simulate renewable energy patterns let intensity, value; // Weekends typically have lower demand const isWeekend = dayOfWeek === 0 || dayOfWeek === 6; // Solar peak hours (10 AM - 4 PM) if (hour >= 10 && hour <= 16) { intensity = 'low'; value = isWeekend ? 150 : 200; } // Evening peak (6 PM - 9 PM) else if (hour >= 18 && hour <= 21) { intensity = 'high'; value = isWeekend ? 350 : 450; } // Night hours else if (hour >= 22 || hour <= 6) { intensity = 'medium'; value = isWeekend ? 250 : 300; } // Other hours else { intensity = 'medium'; value = isWeekend ? 200 : 280; } return { intensity, value, forecast: this.generateForecast(value), timestamp: Date.now() }; } /** * Generate carbon intensity forecast */ generateForecast(currentValue) { const forecast = []; let value = currentValue; for (let i = 1; i <= 24; i++) { // Add some randomness and daily patterns const hour = (new Date().getHours() + i) % 24; const variation = (Math.random() - 0.5) * 50; // Solar peak influence if (hour >= 10 && hour <= 16) { value = Math.max(100, value - 30 + variation); } // Evening peak influence else if (hour >= 18 && hour <= 21) { value = Math.min(500, value + 50 + variation); } // Gradual changes for other hours else { value = Math.max(100, Math.min(400, value + variation)); } forecast.push({ hour: hour, value: Math.round(value), intensity: this.valueToIntensity(value) }); } return forecast; } /** * Convert numeric value to intensity category */ valueToIntensity(value) { if (value < 200) return 'low'; if (value < 350) return 'medium'; return 'high'; } /** * Update device and network conditions */ updateDeviceConditions() { // Network conditions using compatibility layer const navigator = BrowserAPICompat.getNavigator(); const networkInfo = NetworkCompat.getNetworkInfo(); this.conditions.network = networkInfo.effectiveType; this.conditions.connectionType = networkInfo.effectiveType; // Battery conditions if (navigator.getBattery) { navigator.getBattery().then(battery => { this.conditions.battery = battery.level; this.conditions.charging = battery.charging; }).catch(() => { // Fallback for environments without battery API this.conditions.battery = 1.0; // Assume full battery this.conditions.charging = true; }); } else { // Fallback for environments without battery API this.conditions.battery = 1.0; this.conditions.charging = true; } } /** * Determine optimal execution time for a task */ scheduleOptimalExecution(task) { const optimalTime = this.findOptimalExecutionTime(task); const delay = Math.max(0, optimalTime - Date.now()); if (delay === 0) { // Execute immediately this.executeTask(task); } else { // Schedule for later setTimeout(() => { this.executeTask(task); }, delay); } } /** * Find the optimal execution time based on carbon forecast and constraints */ findOptimalExecutionTime(task) { const now = Date.now(); const maxDelay = task.options.maxDelay; const deadline = now + maxDelay; // For urgent tasks, execute immediately if (task.priority === 'urgent') { return now; } // If carbon awareness is disabled, execute based on priority if (!task.options.carbonAware) { return now + this.getPriorityDelay(task.priority); } // Find the best time window in the forecast const forecast = this.carbonData.current.forecast || []; let bestTime = now; let bestScore = this.calculateExecutionScore(now, task); for (const forecastEntry of forecast) { const forecastTime = now + (forecastEntry.hour * 60 * 60 * 1000); // Skip if beyond deadline if (forecastTime > deadline) continue; const score = this.calculateExecutionScore(forecastTime, task, forecastEntry); if (score > bestScore) { bestScore = score; bestTime = forecastTime; } } return bestTime; } /** * Calculate execution score for a given time */ calculateExecutionScore(time, task, forecastEntry = null) { let score = 100; // Base score // Carbon intensity factor (higher score for lower intensity) const intensity = forecastEntry ? forecastEntry.intensity : this.carbonData.current.intensity; switch (intensity) { case 'low': score += 50; break; case 'medium': score += 20; break; case 'high': score -= 30; break; } // Time delay penalty (prefer sooner execution) const delay = time - Date.now(); const delayHours = delay / (60 * 60 * 1000); score -= delayHours * 5; // Network condition factor if (task.options.networkAware) { switch (this.conditions.network) { case '4g': case 'wifi': score += 10; break; case '3g': score += 5; break; case '2g': case 'slow-2g': score -= 20; break; } } // Battery condition factor if (task.options.batteryAware && this.conditions.battery !== 'unknown') { if (this.conditions.charging) { score += 15; } else if (this.conditions.battery < 0.2) { score -= 25; } else if (this.conditions.battery < 0.5) { score -= 10; } } return score; } /** * Execute a task */ async executeTask(task) { try { task.attempts++; task.lastAttempt = Date.now(); console.log(`🚀 Executing task ${task.id} (${task.type}) - Carbon: ${this.carbonData.current.intensity}`); let result; if (task.type === 'batch') { result = await this.executeBatchTask(task); } else { result = await task.execute(task.data); } // Update statistics this.stats.tasksExecuted++; this.updateCarbonSavings(task); // Remove from queue this.removeTaskFromQueue(task); console.log(`✅ Task ${task.id} completed successfully`); return result; } catch (error) { console.error(`❌ Task ${task.id} failed:`, error); // Retry logic if (task.attempts < 3) { const retryDelay = Math.pow(2, task.attempts) * 1000; // Exponential backoff setTimeout(() => { this.executeTask(task); }, retryDelay); } else { this.removeTaskFromQueue(task); throw error; } } } /** * Execute a batch of tasks */ async executeBatchTask(batchTask) { const results = []; const batchSize = batchTask.options.batchSize; for (let i = 0; i < batchTask.tasks.length; i += batchSize) { const batch = batchTask.tasks.slice(i, i + batchSize); const batchResults = await Promise.allSettled( batch.map(task => task.execute(task.data)) ); results.push(...batchResults); // Small delay between batches to prevent overwhelming if (i + batchSize < batchTask.tasks.length) { await new Promise(resolve => setTimeout(resolve, 100)); } } return results; } /** * Utility methods */ generateTaskId() { return `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } getMaxDelayForPriority(priority) { switch (priority) { case 'urgent': return 0; case 'high': return 60 * 60 * 1000; // 1 hour case 'normal': return 6 * 60 * 60 * 1000; // 6 hours case 'low': return 24 * 60 * 60 * 1000; // 24 hours case 'background': return 7 * 24 * 60 * 60 * 1000; // 7 days default: return 6 * 60 * 60 * 1000; } } getPriorityDelay(priority) { switch (priority) { case 'urgent': return 0; case 'high': return 5 * 60 * 1000; // 5 minutes case 'normal': return 30 * 60 * 1000; // 30 minutes case 'low': return 2 * 60 * 60 * 1000; // 2 hours case 'background': return 6 * 60 * 60 * 1000; // 6 hours default: return 30 * 60 * 1000; } } estimateCarbonCost(task) { // Simplified carbon cost estimation const baseCost = 10; // Base carbon cost in grams CO2 const typeFactor = this.getTaskTypeFactor(task.type); const durationFactor = (task.estimatedDuration || 1000) / 1000; return baseCost * typeFactor * durationFactor; } getTaskTypeFactor(taskType) { const factors = { 'sync': 2.0, 'upload': 3.0, 'download': 2.5, 'compute': 1.5, 'batch': 2.5, 'default': 1.0 }; return factors[taskType] || factors.default; } updateCarbonSavings(task) { const currentIntensity = this.carbonData.current.intensity; const estimatedSavings = this.calculateCarbonSavings(task, currentIntensity); this.stats.carbonSaved += estimatedSavings; } calculateCarbonSavings(task, executionIntensity) { // Calculate savings compared to executing during high carbon intensity const highIntensityFactor = 1.5; const currentFactor = executionIntensity === 'low' ? 0.7 : executionIntensity === 'medium' ? 1.0 : 1.3; const potentialCost = task.estimatedCarbonCost * highIntensityFactor; const actualCost = task.estimatedCarbonCost * currentFactor; return Math.max(0, potentialCost - actualCost); } removeTaskFromQueue(task) { for (const priority in this.taskQueue) { const index = this.taskQueue[priority].findIndex(t => t.id === task.id); if (index !== -1) { this.taskQueue[priority].splice(index, 1); break; } } } setupPeriodicUpdates() { // Update carbon data periodically setInterval(() => { this.updateCarbonData(); }, this.carbonData.updateInterval); // Update device conditions periodically setInterval(() => { this.updateDeviceConditions(); }, 5 * 60 * 1000); // Every 5 minutes } startScheduler() { // Process queues periodically setInterval(() => { this.processQueues(); }, 60 * 1000); // Every minute } processQueues() { // Check for tasks that should be executed now for (const priority in this.taskQueue) { const queue = this.taskQueue[priority]; for (const task of queue) { const shouldExecute = this.shouldExecuteNow(task); if (shouldExecute) { this.executeTask(task); } } } } shouldExecuteNow(task) { const now = Date.now(); const deadline = task.scheduledAt + task.options.maxDelay; // Execute if past deadline if (now >= deadline) { return true; } // Execute if conditions are optimal const score = this.calculateExecutionScore(now, task); return score > 80; // Threshold for optimal conditions } useFallbackCarbonData() { this.carbonData.current = this.simulateCarbonIntensity(); } parseCarbonApiResponse(data) { // Parse response from carbon intensity API // This would be customized based on the specific API format return { intensity: this.valueToIntensity(data.intensity), value: data.intensity, forecast: data.forecast || [], timestamp: Date.now() }; } /** * Get scheduler statistics and status */ getStats() { const queueSizes = {}; let totalQueued = 0; for (const priority in this.taskQueue) { queueSizes[priority] = this.taskQueue[priority].length; totalQueued += this.taskQueue[priority].length; } return { ...this.stats, queueSizes, totalQueued, carbonIntensity: this.carbonData.current.intensity, carbonValue: this.carbonData.current.value, lastCarbonUpdate: this.carbonData.lastUpdate, conditions: this.conditions }; } /** * Get current carbon data */ getCarbonData() { return this.carbonData.current; } /** * Force execution of all tasks (emergency mode) */ async forceExecuteAll() { const allTasks = []; for (const priority in this.taskQueue) { allTasks.push(...this.taskQueue[priority]); this.taskQueue[priority] = []; } const results = await Promise.allSettled( allTasks.map(task => this.executeTask(task)) ); return results; } // Smart City Carbon Methods async enableCityWideOptimization(config) { console.log('🌱🏙️ Enabling city-wide carbon optimization...'); this.cityWideOptimization = { ...config, enabled: true }; return this.cityWideOptimization; } async configureRenewableOptimization(config) { console.log('🌱⚡ Configuring renewable energy optimization...'); this.renewableOptimization = { ...config, configured: true }; return this.renewableOptimization; } async enableCarbonNeutralityTracking(config) { console.log('🌱📊 Enabling carbon neutrality tracking...'); this.carbonNeutralityTracking = { ...config, enabled: true }; return this.carbonNeutralityTracking; } async monitorCarbonEmissions() { return { totalEmissions: 1000 + Math.random() * 500, emissionRate: 50 + Math.random() * 30, reductionTarget: 0.5, currentReduction: 0.3 + Math.random() * 0.2 }; } async checkRenewableStatus() { return { solarGeneration: 200 + Math.random() * 100, windGeneration: 150 + Math.random() * 80, renewablePercentage: 0.6 + Math.random() * 0.3, gridStability: 'stable' }; } async calculateSustainabilityMetrics() { return { carbonFootprint: 800 + Math.random() * 400, energyEfficiency: 0.8 + Math.random() * 0.15, renewableRatio: 0.7 + Math.random() * 0.2, sustainabilityScore: 85 + Math.random() * 10 }; } async optimizeEmergencyOperations(alertData) { console.log('🚨 Optimizing emergency operations for carbon efficiency...'); return { emergencyType: alertData.type || 'general', carbonOptimizedRouting: 'enabled', renewableEmergencyPower: 'activated', sustainableResponse: 'prioritized', carbonImpact: 'minimized' }; } /** * Get status (alias for getStats) * @returns {object} Current carbon-aware status */ getStatus() { return this.getStats(); } }