UNPKG

claude-flow-novice

Version:

Claude Flow Novice - Advanced orchestration platform for multi-agent AI workflows with CFN Loop architecture Includes Local RuVector Accelerator and all CFN skills for complete functionality.

388 lines (387 loc) 13.7 kB
/** * Query Optimizer * * Implements database query optimizations including: * - Index management for agents table * - Materialized views for cost aggregation * - Query pattern optimization * - Expected: 10-20x query speedup * * Features: * - Automated index creation * - Materialized view management * - Query rewriting for optimal execution * - Performance monitoring */ export class QueryOptimizer { pool; refreshInterval; refreshTimer = null; constructor(config){ this.pool = config.pool; this.refreshInterval = config.refreshInterval || 3600000; // 1 hour default } /** * Initialize all query optimizations */ async initialize() { console.log('Initializing query optimizer...'); await this.createIndexes(); await this.createMaterializedViews(); await this.startMaterializedViewRefresh(); console.log('Query optimizer initialized successfully'); } /** * Create indexes on agents table for performance * Indexes: team_id, status, spawned_at */ async createIndexes() { const indexes = [ { name: 'idx_agents_team_id', table: 'agents', columns: [ 'team_id' ], description: 'Index for team-based queries' }, { name: 'idx_agents_status', table: 'agents', columns: [ 'status' ], description: 'Index for status filtering' }, { name: 'idx_agents_spawned_at', table: 'agents', columns: [ 'spawned_at' ], description: 'Index for time-based queries' }, { name: 'idx_agents_team_status', table: 'agents', columns: [ 'team_id', 'status' ], description: 'Composite index for team + status queries' }, { name: 'idx_agents_status_spawned', table: 'agents', columns: [ 'status', 'spawned_at' ], description: 'Composite index for status + time queries' }, { name: 'idx_agents_cost_query', table: 'agents', columns: [ 'team_id', 'spawned_at', 'status' ], description: 'Composite index for cost aggregation queries' } ]; const client = await this.pool.connect(); try { for (const index of indexes){ const query = ` CREATE INDEX IF NOT EXISTS ${index.name} ON ${index.table} (${index.columns.join(', ')}) `; await client.query(query); console.log(`Created index: ${index.name} - ${index.description}`); } } finally{ client.release(); } } /** * Create materialized views for cost aggregation queries */ async createMaterializedViews() { const client = await this.pool.connect(); try { // Drop existing views if they exist await client.query('DROP MATERIALIZED VIEW IF EXISTS mv_cost_by_team CASCADE'); await client.query('DROP MATERIALIZED VIEW IF EXISTS mv_cost_by_agent_type CASCADE'); await client.query('DROP MATERIALIZED VIEW IF EXISTS mv_daily_cost_summary CASCADE'); // Create materialized view for cost by team await client.query(` CREATE MATERIALIZED VIEW mv_cost_by_team AS SELECT team_id, COUNT(*) as agent_count, SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed_count, SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed_count, AVG(confidence) as avg_confidence, SUM(COALESCE(metadata::json->>'cost', '0')::numeric) as total_cost, MIN(spawned_at) as first_spawn, MAX(spawned_at) as last_spawn FROM agents WHERE team_id IS NOT NULL GROUP BY team_id `); console.log('Created materialized view: mv_cost_by_team'); // Create materialized view for cost by agent type await client.query(` CREATE MATERIALIZED VIEW mv_cost_by_agent_type AS SELECT type as agent_type, COUNT(*) as agent_count, SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed_count, SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed_count, AVG(confidence) as avg_confidence, SUM(COALESCE(metadata::json->>'cost', '0')::numeric) as total_cost, AVG(EXTRACT(EPOCH FROM (completed_at - spawned_at))) as avg_duration_seconds FROM agents WHERE type IS NOT NULL GROUP BY type `); console.log('Created materialized view: mv_cost_by_agent_type'); // Create materialized view for daily cost summary await client.query(` CREATE MATERIALIZED VIEW mv_daily_cost_summary AS SELECT DATE(spawned_at) as date, COUNT(*) as total_agents, SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed_count, SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed_count, SUM(COALESCE(metadata::json->>'cost', '0')::numeric) as total_cost, AVG(confidence) as avg_confidence FROM agents WHERE spawned_at IS NOT NULL GROUP BY DATE(spawned_at) ORDER BY date DESC `); console.log('Created materialized view: mv_daily_cost_summary'); // Create UNIQUE indexes on materialized views (required for CONCURRENT refresh) await client.query('CREATE UNIQUE INDEX idx_mv_cost_by_team_team_id ON mv_cost_by_team (team_id)'); await client.query('CREATE UNIQUE INDEX idx_mv_cost_by_agent_type_type ON mv_cost_by_agent_type (agent_type)'); await client.query('CREATE UNIQUE INDEX idx_mv_daily_cost_summary_date ON mv_daily_cost_summary (date)'); console.log('Created UNIQUE indexes on materialized views for concurrent refresh'); } finally{ client.release(); } } /** * Refresh materialized views */ async refreshMaterializedViews() { const client = await this.pool.connect(); try { console.log('Refreshing materialized views...'); await client.query('REFRESH MATERIALIZED VIEW CONCURRENTLY mv_cost_by_team'); await client.query('REFRESH MATERIALIZED VIEW CONCURRENTLY mv_cost_by_agent_type'); await client.query('REFRESH MATERIALIZED VIEW CONCURRENTLY mv_daily_cost_summary'); console.log('Materialized views refreshed successfully'); } catch (err) { console.error('Error refreshing materialized views:', err); throw err; } finally{ client.release(); } } /** * Start automatic materialized view refresh */ startMaterializedViewRefresh() { if (this.refreshTimer) { clearInterval(this.refreshTimer); } // Initial refresh this.refreshMaterializedViews().catch(console.error); // Schedule periodic refresh this.refreshTimer = setInterval(()=>{ this.refreshMaterializedViews().catch(console.error); }, this.refreshInterval); console.log(`Started materialized view auto-refresh (interval: ${this.refreshInterval / 1000}s)`); } /** * Stop automatic materialized view refresh */ stopMaterializedViewRefresh() { if (this.refreshTimer) { clearInterval(this.refreshTimer); this.refreshTimer = null; console.log('Stopped materialized view auto-refresh'); } } /** * Optimized query: Get cost by team */ async getCostByTeam(teamId) { const client = await this.pool.connect(); try { let query = 'SELECT * FROM mv_cost_by_team'; const params = []; if (teamId) { query += ' WHERE team_id = $1'; params.push(teamId); } query += ' ORDER BY total_cost DESC'; const result = await client.query(query, params); return result.rows; } finally{ client.release(); } } /** * Optimized query: Get cost by agent type */ async getCostByAgentType(agentType) { const client = await this.pool.connect(); try { let query = 'SELECT * FROM mv_cost_by_agent_type'; const params = []; if (agentType) { query += ' WHERE agent_type = $1'; params.push(agentType); } query += ' ORDER BY total_cost DESC'; const result = await client.query(query, params); return result.rows; } finally{ client.release(); } } /** * Optimized query: Get daily cost summary */ async getDailyCostSummary(startDate, endDate) { const client = await this.pool.connect(); try { let query = 'SELECT * FROM mv_daily_cost_summary'; const params = []; const conditions = []; if (startDate) { params.push(startDate); conditions.push(`date >= $${params.length}`); } if (endDate) { params.push(endDate); conditions.push(`date <= $${params.length}`); } if (conditions.length > 0) { query += ' WHERE ' + conditions.join(' AND '); } query += ' ORDER BY date DESC'; const result = await client.query(query, params); return result.rows; } finally{ client.release(); } } /** * Optimized query: Get agents by team and status * Uses composite index idx_agents_team_status */ async getAgentsByTeamAndStatus(teamId, status) { const client = await this.pool.connect(); try { const query = ` SELECT * FROM agents WHERE team_id = $1 AND status = $2 ORDER BY spawned_at DESC `; const result = await client.query(query, [ teamId, status ]); return result.rows; } finally{ client.release(); } } /** * Optimized query: Get agents by status and time range * Uses composite index idx_agents_status_spawned */ async getAgentsByStatusAndTimeRange(status, startDate, endDate) { const client = await this.pool.connect(); try { const query = ` SELECT * FROM agents WHERE status = $1 AND spawned_at >= $2 AND spawned_at <= $3 ORDER BY spawned_at DESC `; const result = await client.query(query, [ status, startDate, endDate ]); return result.rows; } finally{ client.release(); } } /** * Analyze query performance */ async analyzeQuery(query, params) { const client = await this.pool.connect(); try { const explainQuery = `EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) ${query}`; const result = await client.query(explainQuery, params); return result.rows[0]['QUERY PLAN'][0]; } finally{ client.release(); } } /** * Get index usage statistics */ async getIndexUsageStats() { const client = await this.pool.connect(); try { const query = ` SELECT schemaname, tablename, indexname, idx_scan as index_scans, idx_tup_read as tuples_read, idx_tup_fetch as tuples_fetched FROM pg_stat_user_indexes WHERE schemaname = 'public' ORDER BY idx_scan DESC `; const result = await client.query(query); return result.rows; } finally{ client.release(); } } /** * Shutdown query optimizer */ async shutdown() { this.stopMaterializedViewRefresh(); console.log('Query optimizer shutdown complete'); } } // Singleton instance let queryOptimizerInstance = null; /** * Initialize singleton query optimizer */ export async function initQueryOptimizer(config) { if (!queryOptimizerInstance) { queryOptimizerInstance = new QueryOptimizer(config); await queryOptimizerInstance.initialize(); } return queryOptimizerInstance; } /** * Get singleton query optimizer instance */ export function getQueryOptimizer() { if (!queryOptimizerInstance) { throw new Error('Query optimizer not initialized. Call initQueryOptimizer first.'); } return queryOptimizerInstance; } /** * Shutdown singleton query optimizer */ export async function shutdownQueryOptimizer() { if (queryOptimizerInstance) { await queryOptimizerInstance.shutdown(); queryOptimizerInstance = null; } } //# sourceMappingURL=query-optimizer.js.map