UNPKG

syntropylog

Version:

An instance manager with observability for Node.js applications

343 lines 14 kB
import { SerializationComplexity, } from './types'; import { DataSanitizer } from './utils/DataSanitizer'; export class SerializationPipeline { steps = []; timeoutStrategies = new Map(); sanitizer; metrics = null; constructor() { this.sanitizer = new DataSanitizer(); this.initializeDefaultStrategies(); } addStep(step) { this.steps.push(step); } addTimeoutStrategy(strategy) { this.timeoutStrategies.set(strategy.getStrategyName(), strategy); } async process(data, context) { const pipelineStartTime = Date.now(); this.metrics = { stepDurations: {}, totalDuration: 0, operationTimeout: 0, timeoutStrategy: 'unknown', }; let currentData = data; try { // Ejecutar SerializationStep primero const serializationStep = this.steps.find((step) => step.name === 'serialization'); if (serializationStep) { const stepStartTime = Date.now(); currentData = await serializationStep.execute(currentData, context); const stepDuration = Date.now() - stepStartTime; this.metrics.stepDurations[serializationStep.name] = stepDuration; } // Si llegamos aquí, la serialización fue exitosa // Continuar con los otros steps for (const step of this.steps) { if (step.name === 'serialization') continue; // Ya se ejecutó const stepStartTime = Date.now(); currentData = await step.execute(currentData, context); const stepDuration = Date.now() - stepStartTime; this.metrics.stepDurations[step.name] = stepDuration; } // Calcular timeout de operación basado en el resultado const timeoutStrategy = this.selectTimeoutStrategy(currentData); const operationTimeout = timeoutStrategy.calculateTimeout(currentData); this.metrics.operationTimeout = operationTimeout; this.metrics.timeoutStrategy = timeoutStrategy.getStrategyName(); this.metrics.totalDuration = Date.now() - pipelineStartTime; // Obtener la complejidad real del serializador const actualComplexity = currentData.serializationComplexity || SerializationComplexity.SIMPLE; return { success: true, data: currentData, serializer: currentData.serializer || 'pipeline', duration: this.metrics.totalDuration, complexity: actualComplexity, sanitized: context.sanitizeSensitiveData, metadata: { stepDurations: this.metrics.stepDurations, operationTimeout, timeoutStrategy: timeoutStrategy.getStrategyName(), serializer: currentData.serializer || 'pipeline', complexity: actualComplexity, }, }; } catch (error) { this.metrics.totalDuration = Date.now() - pipelineStartTime; // Preservar el nombre del serializador del error const serializerName = error.serializer || 'pipeline'; return { success: false, data: currentData, serializer: serializerName, duration: this.metrics.totalDuration, complexity: SerializationComplexity.SIMPLE, sanitized: false, error: error instanceof Error ? error.message : String(error), metadata: { stepDurations: this.metrics.stepDurations, operationTimeout: 0, timeoutStrategy: 'unknown', serializer: serializerName, complexity: SerializationComplexity.SIMPLE, }, }; } } getMetrics() { return this.metrics; } selectTimeoutStrategy(data) { // Seleccionar estrategia basada en el tipo de datos if (data?.type === 'PrismaQuery') { return (this.timeoutStrategies.get('prisma') || this.timeoutStrategies.get('default')); } if (data?.type === 'TypeORMQuery') { return (this.timeoutStrategies.get('typeorm') || this.timeoutStrategies.get('default')); } if (data?.type === 'MySQLQuery') { return (this.timeoutStrategies.get('mysql') || this.timeoutStrategies.get('default')); } if (data?.type === 'PostgreSQLQuery') { return (this.timeoutStrategies.get('postgresql') || this.timeoutStrategies.get('default')); } if (data?.type === 'SQLServerQuery') { return (this.timeoutStrategies.get('sqlserver') || this.timeoutStrategies.get('default')); } if (data?.type === 'OracleQuery') { return (this.timeoutStrategies.get('oracle') || this.timeoutStrategies.get('default')); } return this.timeoutStrategies.get('default'); } initializeDefaultStrategies() { // Estrategia por defecto this.addTimeoutStrategy(new DefaultTimeoutStrategy()); // Estrategias específicas por base de datos this.addTimeoutStrategy(new PrismaTimeoutStrategy()); this.addTimeoutStrategy(new TypeORMTimeoutStrategy()); this.addTimeoutStrategy(new MySQLTimeoutStrategy()); this.addTimeoutStrategy(new PostgreSQLTimeoutStrategy()); this.addTimeoutStrategy(new SQLServerTimeoutStrategy()); this.addTimeoutStrategy(new OracleTimeoutStrategy()); } } export class DefaultTimeoutStrategy { calculateTimeout(data) { return 5000; // 5 segundos por defecto } getStrategyName() { return 'default'; } } export class PrismaTimeoutStrategy { calculateTimeout(data) { // Lógica específica para Prisma if (data?.operation === 'findMany') { return 10000; // 10 segundos para consultas múltiples } if (data?.operation === 'aggregate') { return 15000; // 15 segundos para agregaciones } if (data?.operation === 'createMany') { return 20000; // 20 segundos para inserciones masivas } if (data?.operation === 'updateMany') { return 15000; // 15 segundos para actualizaciones masivas } if (data?.operation === 'deleteMany') { return 10000; // 10 segundos para eliminaciones masivas } if (data?.operation === 'findFirst') { return 5000; // 5 segundos para consultas simples } if (data?.operation === 'findUnique') { return 3000; // 3 segundos para consultas por clave única } if (data?.operation === 'create') { return 5000; // 5 segundos para inserciones simples } if (data?.operation === 'update') { return 5000; // 5 segundos para actualizaciones simples } if (data?.operation === 'delete') { return 3000; // 3 segundos para eliminaciones simples } return 8000; // 8 segundos por defecto para Prisma } getStrategyName() { return 'prisma'; } } export class TypeORMTimeoutStrategy { calculateTimeout(data) { // Lógica específica para TypeORM if (data?.operation === 'find') { return 8000; // 8 segundos para consultas } if (data?.operation === 'save') { return 10000; // 10 segundos para guardar } return 7000; // 7 segundos por defecto para TypeORM } getStrategyName() { return 'typeorm'; } } export class MySQLTimeoutStrategy { calculateTimeout(data) { // Lógica específica para MySQL if (data?.query?.toLowerCase().includes('select')) { if (data?.query?.toLowerCase().includes('count(*)')) { return 12000; // 12 segundos para COUNT } if (data?.query?.toLowerCase().includes('group by')) { return 15000; // 15 segundos para GROUP BY } if (data?.query?.toLowerCase().includes('order by')) { return 10000; // 10 segundos para ORDER BY } if (data?.query?.toLowerCase().includes('limit')) { return 8000; // 8 segundos para consultas con LIMIT } return 6000; // 6 segundos para SELECT simples } if (data?.query?.toLowerCase().includes('insert')) { if (data?.query?.toLowerCase().includes('values')) { const valuesCount = (data.query.match(/values/gi) || []).length; return Math.min(5000 + valuesCount * 100, 20000); // 5-20 segundos basado en cantidad de valores } return 8000; // 8 segundos para INSERT simples } if (data?.query?.toLowerCase().includes('update')) { return 10000; // 10 segundos para UPDATE } if (data?.query?.toLowerCase().includes('delete')) { return 8000; // 8 segundos para DELETE } return 7000; // 7 segundos por defecto para MySQL } getStrategyName() { return 'mysql'; } } export class PostgreSQLTimeoutStrategy { calculateTimeout(data) { // Lógica específica para PostgreSQL if (data?.query?.toLowerCase().includes('select')) { if (data?.query?.toLowerCase().includes('count(*)')) { return 10000; // 10 segundos para COUNT } if (data?.query?.toLowerCase().includes('group by')) { return 12000; // 12 segundos para GROUP BY } if (data?.query?.toLowerCase().includes('order by')) { return 8000; // 8 segundos para ORDER BY } if (data?.query?.toLowerCase().includes('limit')) { return 6000; // 6 segundos para consultas con LIMIT } if (data?.query?.toLowerCase().includes('window')) { return 15000; // 15 segundos para window functions } if (data?.query?.toLowerCase().includes('cte')) { return 18000; // 18 segundos para CTEs } return 5000; // 5 segundos para SELECT simples } if (data?.query?.toLowerCase().includes('insert')) { if (data?.query?.toLowerCase().includes('values')) { const valuesCount = (data.query.match(/values/gi) || []).length; return Math.min(4000 + valuesCount * 80, 15000); // 4-15 segundos basado en cantidad de valores } return 6000; // 6 segundos para INSERT simples } if (data?.query?.toLowerCase().includes('update')) { return 8000; // 8 segundos para UPDATE } if (data?.query?.toLowerCase().includes('delete')) { return 6000; // 6 segundos para DELETE } return 6000; // 6 segundos por defecto para PostgreSQL } getStrategyName() { return 'postgresql'; } } export class SQLServerTimeoutStrategy { calculateTimeout(data) { // Lógica específica para SQL Server if (data?.query?.toLowerCase().includes('select')) { if (data?.query?.toLowerCase().includes('count(*)')) { return 12000; // 12 segundos para COUNT } if (data?.query?.toLowerCase().includes('group by')) { return 15000; // 15 segundos para GROUP BY } if (data?.query?.toLowerCase().includes('order by')) { return 10000; // 10 segundos para ORDER BY } return 8000; // 8 segundos para SELECT simples } if (data?.query?.toLowerCase().includes('insert')) { return 10000; // 10 segundos para INSERT } if (data?.query?.toLowerCase().includes('update')) { return 12000; // 12 segundos para UPDATE } if (data?.query?.toLowerCase().includes('delete')) { return 10000; // 10 segundos para DELETE } return 9000; // 9 segundos por defecto para SQL Server } getStrategyName() { return 'sqlserver'; } } export class OracleTimeoutStrategy { calculateTimeout(data) { // Lógica específica para Oracle if (data?.query?.toLowerCase().includes('select')) { if (data?.query?.toLowerCase().includes('count(*)')) { return 15000; // 15 segundos para COUNT } if (data?.query?.toLowerCase().includes('group by')) { return 18000; // 18 segundos para GROUP BY } if (data?.query?.toLowerCase().includes('order by')) { return 12000; // 12 segundos para ORDER BY } if (data?.query?.toLowerCase().includes('rownum')) { return 10000; // 10 segundos para consultas con ROWNUM } if (data?.query?.toLowerCase().includes('connect by')) { return 20000; // 20 segundos para consultas jerárquicas } return 10000; // 10 segundos para SELECT simples } if (data?.query?.toLowerCase().includes('insert')) { return 12000; // 12 segundos para INSERT } if (data?.query?.toLowerCase().includes('update')) { return 15000; // 15 segundos para UPDATE } if (data?.query?.toLowerCase().includes('delete')) { return 12000; // 12 segundos para DELETE } return 12000; // 12 segundos por defecto para Oracle } getStrategyName() { return 'oracle'; } } //# sourceMappingURL=SerializationPipeline.js.map