UNPKG

cs-element

Version:

Advanced reactive data management library with state machines, blueprints, persistence, compression, networking, and multithreading support

972 lines (806 loc) 33.1 kB
# 🚀 Batch операции и массовые операции CSElement предоставляет мощную систему для выполнения массовых операций с оптимизацией производительности и гибким управлением выполнением. ## 📋 Содержание 1. [Основы batch операций](#основы-batch-операций) 2. [Создание и управление батчами](#создание-и-управление-батчами) 3. [Типы операций](#типы-операций) 4. [Стратегии выполнения](#стратегии-выполнения) 5. [Обработка ошибок](#обработка-ошибок) 6. [Мониторинг и прогресс](#мониторинг-и-прогресс) 7. [Оптимизация производительности](#оптимизация-производительности) 8. [Полный пример: Система импорта данных](#полный-пример-система-импорта-данных) ## Основы batch операций Batch операции позволяют группировать множество операций и выполнять их эффективно с контролем производительности, обработкой ошибок и отслеживанием прогресса. ### Простой пример ```typescript import { CSElement, BatchManager, BatchOperationType, BatchPriority } from 'cs-element'; // Создаем менеджер батчей const batchManager = new BatchManager(); // Создаем новый батч const batchId = batchManager.createBatch({ executionStrategy: 'parallel', maxConcurrency: 5 }); // Добавляем операции const operations = [ { id: 'create-user-1', type: BatchOperationType.CREATE, priority: BatchPriority.NORMAL, execute: async () => { const user = new CSElement('user-1'); await user.setData('name', 'Алексей'); await user.setData('email', 'alexey@example.com'); return user; } }, { id: 'create-user-2', type: BatchOperationType.CREATE, priority: BatchPriority.NORMAL, execute: async () => { const user = new CSElement('user-2'); await user.setData('name', 'Мария'); await user.setData('email', 'maria@example.com'); return user; } } ]; batchManager.addOperations(batchId, operations); // Выполняем батч const result = await batchManager.executeBatch(batchId); console.log(`Батч выполнен: ${result.success}`); console.log(`Операций выполнено: ${result.operationResults.length}`); ``` ## Создание и управление батчами ### Конфигурация батча ```typescript import { BatchExecutionStrategy, BatchErrorMode, BatchConfig } from 'cs-element'; const config: BatchConfig = { executionStrategy: BatchExecutionStrategy.PARALLEL, errorMode: BatchErrorMode.COLLECT_ERRORS, maxConcurrency: 10, timeout: 300000, // 5 минут progressUpdateInterval: 1000, // 1 секунда enableRollback: true, validateBeforeExecution: true, persistIntermediateResults: false }; const batchId = batchManager.createBatch(config); ``` ### Управление жизненным циклом ```typescript // Создание батча const batchId = batchManager.createBatch(); // Проверка статуса const progress = batchManager.getProgress(batchId); console.log(`Прогресс: ${progress?.percentage}%`); // Отмена выполнения await batchManager.cancelBatch(batchId); // Получение результата const result = batchManager.getBatchResult(batchId); // Удаление батча batchManager.removeBatch(batchId); ``` ## Типы операций ### Базовые операции с элементами ```typescript import { DefaultBatchOperationFactory } from 'cs-element'; const factory = new DefaultBatchOperationFactory(); const root = new CSElement('root'); // Операция создания элемента const createOp = factory.createElementOperation({ name: 'new-element', data: { type: 'document', status: 'draft' } }); // Операция обновления элемента const element = await root.addElement('existing-element'); const updateOp = factory.updateElementOperation(element, { status: 'published', lastModified: new Date() }); // Операция удаления элемента const deleteOp = factory.deleteElementOperation(element); // Операция перемещения элемента const newParent = new CSElement('new-parent'); const moveOp = factory.moveElementOperation(element, newParent); // Операция копирования элемента const copyOp = factory.copyElementOperation(element, newParent); ``` ### Пользовательские операции ```typescript // Сложная пользовательская операция const customOperation = { id: 'complex-data-processing', type: BatchOperationType.CUSTOM, priority: BatchPriority.HIGH, timeout: 60000, maxRetries: 3, validate: async (context) => { // Валидация перед выполнением const hasRequiredData = context.sharedData.has('inputData'); return { valid: hasRequiredData, errors: hasRequiredData ? [] : ['Отсутствуют входные данные'], warnings: [] }; }, execute: async (context) => { const inputData = context.sharedData.get('inputData'); // Обновляем прогресс context.updateProgress?.({ currentOperation: 'Обработка данных...' }); // Выполняем обработку const processedData = await processComplexData(inputData); // Сохраняем результат для других операций context.sharedData.set('processedData', processedData); return processedData; }, rollback: async (context) => { // Откат изменений при ошибке context.sharedData.delete('processedData'); console.log('Откат операции обработки данных'); } }; async function processComplexData(data: any): Promise<any> { // Симуляция сложной обработки return new Promise(resolve => { setTimeout(() => { resolve({ ...data, processed: true, timestamp: Date.now() }); }, 2000); }); } ``` ## Стратегии выполнения ### Параллельное выполнение ```typescript // Быстрое выполнение независимых операций const parallelBatch = batchManager.createBatch({ executionStrategy: BatchExecutionStrategy.PARALLEL, maxConcurrency: 8 }); const parallelOps = Array.from({ length: 20 }, (_, i) => ({ id: `parallel-op-${i}`, type: BatchOperationType.CREATE, priority: BatchPriority.NORMAL, execute: async () => { const element = new CSElement(`element-${i}`); await element.setData('index', i); await element.setData('created', new Date()); return element; } })); batchManager.addOperations(parallelBatch, parallelOps); const parallelResult = await batchManager.executeBatch(parallelBatch); ``` ### Последовательное выполнение ```typescript // Операции с зависимостями const sequentialBatch = batchManager.createBatch({ executionStrategy: BatchExecutionStrategy.SEQUENTIAL }); const sequentialOps = [ { id: 'step-1', type: BatchOperationType.CREATE, priority: BatchPriority.HIGH, execute: async (context) => { const config = new CSElement('config'); await config.setData('database', 'initialized'); context.sharedData.set('config', config); return config; } }, { id: 'step-2', type: BatchOperationType.CREATE, priority: BatchPriority.NORMAL, dependencies: ['step-1'], execute: async (context) => { const config = context.sharedData.get('config'); const users = new CSElement('users'); await users.setData('configRef', config.id); context.sharedData.set('users', users); return users; } }, { id: 'step-3', type: BatchOperationType.UPDATE, priority: BatchPriority.NORMAL, dependencies: ['step-1', 'step-2'], execute: async (context) => { const config = context.sharedData.get('config'); const users = context.sharedData.get('users'); await config.setData('usersRef', users.id); await config.setData('status', 'ready'); return { config, users }; } } ]; batchManager.addOperations(sequentialBatch, sequentialOps); ``` ### Смешанная стратегия ```typescript // Комбинация параллельного и последовательного выполнения const mixedBatch = batchManager.createBatch({ executionStrategy: BatchExecutionStrategy.MIXED, maxConcurrency: 4 }); const mixedOps = [ // Группа 1: Параллельная инициализация { id: 'init-database', type: BatchOperationType.CREATE, priority: BatchPriority.CRITICAL, execute: async () => { const db = new CSElement('database'); await db.setData('status', 'initializing'); await new Promise(resolve => setTimeout(resolve, 1000)); await db.setData('status', 'ready'); return db; } }, { id: 'init-cache', type: BatchOperationType.CREATE, priority: BatchPriority.CRITICAL, execute: async () => { const cache = new CSElement('cache'); await cache.setData('status', 'initializing'); await new Promise(resolve => setTimeout(resolve, 800)); await cache.setData('status', 'ready'); return cache; } }, // Группа 2: Зависимые операции (выполняются после группы 1) { id: 'create-users', type: BatchOperationType.CREATE, priority: BatchPriority.HIGH, dependencies: ['init-database'], execute: async () => { const users = new CSElement('users'); await users.setData('table', 'users'); return users; } }, { id: 'create-sessions', type: BatchOperationType.CREATE, priority: BatchPriority.HIGH, dependencies: ['init-cache'], execute: async () => { const sessions = new CSElement('sessions'); await sessions.setData('storage', 'cache'); return sessions; } } ]; batchManager.addOperations(mixedBatch, mixedOps); ``` ## Обработка ошибок ### Режимы обработки ошибок ```typescript // Остановка при первой ошибке const failFastBatch = batchManager.createBatch({ errorMode: BatchErrorMode.FAIL_FAST }); // Продолжение выполнения несмотря на ошибки const continueBatch = batchManager.createBatch({ errorMode: BatchErrorMode.CONTINUE }); // Сбор всех ошибок const collectErrorsBatch = batchManager.createBatch({ errorMode: BatchErrorMode.COLLECT_ERRORS }); ``` ### Операции с обработкой ошибок ```typescript const resilientOp = { id: 'resilient-operation', type: BatchOperationType.CUSTOM, priority: BatchPriority.NORMAL, maxRetries: 3, execute: async (context) => { const attempt = context.operation.metadata?.attempt || 1; try { // Операция, которая может завершиться ошибкой if (Math.random() < 0.7 && attempt < 3) { throw new Error(`Временная ошибка (попытка ${attempt})`); } return { success: true, attempt }; } catch (error) { console.log(`Ошибка в попытке ${attempt}: ${error.message}`); if (attempt >= 3) { // Последняя попытка - выполняем fallback return { success: false, fallback: true }; } throw error; // Повторить попытку } }, rollback: async (context) => { console.log('Выполняем откат операции'); // Логика отката } }; ``` ## Мониторинг и прогресс ### Отслеживание прогресса ```typescript // Подписка на события батча batchManager.addEventListener({ onBatchStart: (batchId) => { console.log(`🚀 Начало выполнения батча: ${batchId}`); }, onBatchComplete: (result) => { console.log(`✅ Батч завершен: ${result.batchId}`); console.log(`Успешно: ${result.success}`); console.log(`Время выполнения: ${result.totalExecutionTime}мс`); }, onOperationStart: (operation) => { console.log(`▶️ Начало операции: ${operation.id}`); }, onOperationComplete: (result) => { console.log(`✔️ Операция завершена: ${result.operationId}`); }, onProgressUpdate: (progress) => { console.log(`📊 Прогресс: ${progress.percentage}% (${progress.completedOperations}/${progress.totalOperations})`); if (progress.estimatedTimeRemaining) { console.log(`⏱️ Осталось: ${Math.round(progress.estimatedTimeRemaining / 1000)}с`); } }, onBatchError: (batchId, error) => { console.error(`❌ Ошибка в батче ${batchId}:`, error.message); } }); ``` ### Статистика выполнения ```typescript // Получение статистики менеджера const stats = batchManager.getStatistics(); console.log('📈 Статистика BatchManager:'); console.log(`Всего батчей: ${stats.totalBatches}`); console.log(`Успешных: ${stats.successfulBatches}`); console.log(`Неудачных: ${stats.failedBatches}`); console.log(`Активных: ${stats.activeBatches}`); console.log(`Среднее время выполнения: ${stats.averageBatchExecutionTime}мс`); // Статистика конкретного батча const batchResult = batchManager.getBatchResult(batchId); if (batchResult) { console.log('📊 Статистика батча:'); console.log(`Операций: ${batchResult.statistics.totalOperations}`); console.log(`Успешных: ${batchResult.statistics.successfulOperations}`); console.log(`Неудачных: ${batchResult.statistics.failedOperations}`); console.log(`Среднее время операции: ${batchResult.statistics.averageExecutionTime}мс`); } ``` ## Оптимизация производительности ### Настройка параллелизма ```typescript // Оптимальная настройка для разных типов операций const cpuIntensiveBatch = batchManager.createBatch({ executionStrategy: BatchExecutionStrategy.PARALLEL, maxConcurrency: Math.max(1, Math.floor(navigator.hardwareConcurrency * 0.8)) }); const ioIntensiveBatch = batchManager.createBatch({ executionStrategy: BatchExecutionStrategy.PARALLEL, maxConcurrency: 20 // Больше для I/O операций }); const mixedWorkloadBatch = batchManager.createBatch({ executionStrategy: BatchExecutionStrategy.MIXED, maxConcurrency: 6 }); ``` ### Группировка операций ```typescript // Группировка схожих операций для лучшей производительности const groupedOperations = [ // Группа 1: Быстрые операции создания ...Array.from({ length: 100 }, (_, i) => ({ id: `quick-create-${i}`, type: BatchOperationType.CREATE, priority: BatchPriority.NORMAL, execute: async () => { const element = new CSElement(`quick-${i}`); await element.setData('type', 'quick'); return element; } })), // Группа 2: Медленные операции обработки ...Array.from({ length: 10 }, (_, i) => ({ id: `slow-process-${i}`, type: BatchOperationType.TRANSFORM, priority: BatchPriority.LOW, execute: async () => { // Симуляция тяжелой обработки await new Promise(resolve => setTimeout(resolve, 2000)); return { processed: i }; } })) ]; ``` ### Управление памятью ```typescript const memoryOptimizedBatch = batchManager.createBatch({ executionStrategy: BatchExecutionStrategy.SEQUENTIAL, persistIntermediateResults: false, // Не сохраняем промежуточные результаты progressUpdateInterval: 5000 // Реже обновляем прогресс }); // Операция с очисткой памяти const memoryEfficientOp = { id: 'memory-efficient', type: BatchOperationType.CUSTOM, priority: BatchPriority.NORMAL, execute: async (context) => { try { // Обработка данных const result = await processLargeDataset(); // Очистка промежуточных данных context.sharedData.clear(); return result; } finally { // Принудительная сборка мусора (если доступна) if (global.gc) { global.gc(); } } } }; async function processLargeDataset(): Promise<any> { // Симуляция обработки большого объема данных return { size: 'large', processed: true }; } ``` ## Полный пример: Система импорта данных ```typescript import { CSElement, BatchManager, DefaultBatchOperationFactory, BatchOperationType, BatchPriority, BatchExecutionStrategy, BatchErrorMode } from 'cs-element'; interface ImportData { users: Array<{ id: string; name: string; email: string; department: string; }>; departments: Array<{ id: string; name: string; description: string; }>; projects: Array<{ id: string; name: string; departmentId: string; memberIds: string[]; }>; } class DataImportSystem { private batchManager: BatchManager; private factory: DefaultBatchOperationFactory; private root: CSElement; constructor() { this.batchManager = new BatchManager(); this.factory = new DefaultBatchOperationFactory(); this.root = new CSElement('company'); } async importData(data: ImportData): Promise<{ success: boolean; imported: { departments: number; users: number; projects: number; }; errors: string[]; }> { console.log('🚀 Начинаем импорт данных...'); // Создаем батч с оптимальной конфигурацией const batchId = this.batchManager.createBatch({ executionStrategy: BatchExecutionStrategy.MIXED, errorMode: BatchErrorMode.COLLECT_ERRORS, maxConcurrency: 6, timeout: 600000, // 10 минут enableRollback: true, validateBeforeExecution: true }); // Настраиваем мониторинг this.setupProgressMonitoring(batchId); try { // Создаем операции импорта const operations = [ ...this.createDepartmentOperations(data.departments), ...this.createUserOperations(data.users), ...this.createProjectOperations(data.projects), this.createValidationOperation(), this.createIndexingOperation() ]; this.batchManager.addOperations(batchId, operations); // Выполняем импорт const result = await this.batchManager.executeBatch(batchId); // Анализируем результаты const stats = this.analyzeResults(result); console.log('✅ Импорт завершен!'); console.log(`📊 Статистика: ${JSON.stringify(stats, null, 2)}`); return { success: result.success, imported: stats.imported, errors: result.errors.map(e => e.message) }; } catch (error) { console.error('❌ Критическая ошибка импорта:', error); return { success: false, imported: { departments: 0, users: 0, projects: 0 }, errors: [error.message] }; } finally { // Очистка ресурсов this.batchManager.removeBatch(batchId); } } private createDepartmentOperations(departments: ImportData['departments']) { return departments.map(dept => ({ id: `import-dept-${dept.id}`, type: BatchOperationType.CREATE, priority: BatchPriority.HIGH, // Высокий приоритет - другие зависят от отделов validate: async () => ({ valid: !!dept.name && !!dept.id, errors: [ !dept.id && 'Отсутствует ID отдела', !dept.name && 'Отсутствует название отдела' ].filter(Boolean), warnings: [] }), execute: async (context) => { context.updateProgress?.({ currentOperation: `Создание отдела: ${dept.name}` }); const department = await this.root.addElement(`dept-${dept.id}`) as CSElement; await department.setData('name', dept.name); await department.setData('description', dept.description); await department.setData('type', 'department'); await department.setData('importId', dept.id); // Сохраняем для использования в других операциях context.sharedData.set(`dept-${dept.id}`, department); return department; }, rollback: async (context) => { const department = context.sharedData.get(`dept-${dept.id}`); if (department) { await this.root.removeElement(department); context.sharedData.delete(`dept-${dept.id}`); } } })); } private createUserOperations(users: ImportData['users']) { return users.map(user => ({ id: `import-user-${user.id}`, type: BatchOperationType.CREATE, priority: BatchPriority.NORMAL, dependencies: [`import-dept-${user.department}`], // Зависит от создания отдела execute: async (context) => { context.updateProgress?.({ currentOperation: `Создание пользователя: ${user.name}` }); const department = context.sharedData.get(`dept-${user.department}`); if (!department) { throw new Error(`Отдел ${user.department} не найден для пользователя ${user.name}`); } const userElement = await department.addElement(`user-${user.id}`) as CSElement; await userElement.setData('name', user.name); await userElement.setData('email', user.email); await userElement.setData('type', 'user'); await userElement.setData('importId', user.id); context.sharedData.set(`user-${user.id}`, userElement); return userElement; } })); } private createProjectOperations(projects: ImportData['projects']) { return projects.map(project => ({ id: `import-project-${project.id}`, type: BatchOperationType.CREATE, priority: BatchPriority.NORMAL, dependencies: [ `import-dept-${project.departmentId}`, ...project.memberIds.map(id => `import-user-${id}`) ], execute: async (context) => { context.updateProgress?.({ currentOperation: `Создание проекта: ${project.name}` }); const department = context.sharedData.get(`dept-${project.departmentId}`); const projectElement = await department.addElement(`project-${project.id}`) as CSElement; await projectElement.setData('name', project.name); await projectElement.setData('type', 'project'); await projectElement.setData('importId', project.id); // Связываем с участниками for (const memberId of project.memberIds) { const user = context.sharedData.get(`user-${memberId}`); if (user) { await projectElement.addElement(user); } } return projectElement; } })); } private createValidationOperation() { return { id: 'validate-import', type: BatchOperationType.VALIDATE, priority: BatchPriority.LOW, dependencies: [], // Выполняется в конце execute: async (context) => { context.updateProgress?.({ currentOperation: 'Валидация импортированных данных' }); const departments = this.root.query('[type="department"]'); const users = this.root.query('[type="user"]'); const projects = this.root.query('[type="project"]'); const validation = { departments: departments.length, users: users.length, projects: projects.length, issues: [] }; // Проверяем целостность данных for (const project of projects) { const members = project.getAllElements().filter(e => e.getData('type') === 'user' ); if (members.length === 0) { validation.issues.push(`Проект ${project.getData('name')} не имеет участников`); } } console.log('🔍 Результаты валидации:', validation); return validation; } }; } private createIndexingOperation() { return { id: 'create-indexes', type: BatchOperationType.CUSTOM, priority: BatchPriority.LOW, execute: async (context) => { context.updateProgress?.({ currentOperation: 'Создание индексов для быстрого поиска' }); // Создаем индексы для быстрого поиска const emailIndex = new CSElement('email-index'); const departmentIndex = new CSElement('department-index'); await this.root.addElement(emailIndex); await this.root.addElement(departmentIndex); // Индексируем пользователей по email const users = this.root.query('[type="user"]'); for (const user of users) { const email = user.getData('email'); if (email) { await emailIndex.setData(email, user.id); } } // Индексируем по отделам const departments = this.root.query('[type="department"]'); for (const dept of departments) { const name = dept.getData('name'); if (name) { await departmentIndex.setData(name, dept.id); } } return { emailIndex: emailIndex.id, departmentIndex: departmentIndex.id }; } }; } private setupProgressMonitoring(batchId: string) { this.batchManager.addEventListener({ onProgressUpdate: (progress) => { const percentage = Math.round(progress.percentage); const completed = progress.completedOperations; const total = progress.totalOperations; const current = progress.currentOperation || 'Выполнение операций'; console.log(`📊 ${percentage}% (${completed}/${total}) - ${current}`); if (progress.estimatedTimeRemaining) { const minutes = Math.round(progress.estimatedTimeRemaining / 60000); console.log(`⏱️ Осталось примерно: ${minutes} мин`); } }, onOperationComplete: (result) => { if (result.success) { console.log(`✔️ ${result.operationId} (${result.executionTime}мс)`); } else { console.log(`❌ ${result.operationId}: ${result.error?.message}`); } } }); } private analyzeResults(result: any) { const imported = { departments: 0, users: 0, projects: 0 }; for (const opResult of result.operationResults) { if (opResult.success) { if (opResult.operationId.startsWith('import-dept-')) { imported.departments++; } else if (opResult.operationId.startsWith('import-user-')) { imported.users++; } else if (opResult.operationId.startsWith('import-project-')) { imported.projects++; } } } return { imported, totalTime: result.totalExecutionTime, averageOpTime: result.statistics.averageExecutionTime, successRate: (result.statistics.successfulOperations / result.statistics.totalOperations) * 100 }; } } // Использование системы импорта async function demonstrateImport() { const importSystem = new DataImportSystem(); const sampleData: ImportData = { departments: [ { id: 'IT', name: 'IT отдел', description: 'Информационные технологии' }, { id: 'HR', name: 'HR отдел', description: 'Управление персоналом' }, { id: 'SALES', name: 'Отдел продаж', description: 'Продажи и маркетинг' } ], users: [ { id: '1', name: 'Алексей Петров', email: 'alexey@company.com', department: 'IT' }, { id: '2', name: 'Мария Иванова', email: 'maria@company.com', department: 'HR' }, { id: '3', name: 'Сергей Сидоров', email: 'sergey@company.com', department: 'IT' }, { id: '4', name: 'Анна Козлова', email: 'anna@company.com', department: 'SALES' } ], projects: [ { id: 'P1', name: 'Новая CRM система', departmentId: 'IT', memberIds: ['1', '3'] }, { id: 'P2', name: 'Кампания по найму', departmentId: 'HR', memberIds: ['2'] } ] }; const result = await importSystem.importData(sampleData); console.log('🎉 Результат импорта:'); console.log(`Успешно: ${result.success}`); console.log(`Импортировано отделов: ${result.imported.departments}`); console.log(`Импортировано пользователей: ${result.imported.users}`); console.log(`Импортировано проектов: ${result.imported.projects}`); if (result.errors.length > 0) { console.log('❌ Ошибки:', result.errors); } } // demonstrateImport(); ``` ## 🎯 Заключение Система batch операций CSElement предоставляет: - **🚀 Высокую производительность** - оптимизированное выполнение массовых операций - **🔧 Гибкую конфигурацию** - различные стратегии выполнения и обработки ошибок - **📊 Подробный мониторинг** - отслеживание прогресса и статистики - **🛡️ Надежность** - обработка ошибок, откат операций и валидация - **⚡ Масштабируемость** - эффективная работа с большими объемами данных Используйте batch операции для импорта данных, массовых обновлений, миграций и других задач, требующих обработки множества элементов. --- **Следующий раздел:** [React интеграция](06-react-integration.md) - изучите, как интегрировать CSElement с React приложениями.