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
JavaScript
/**
* 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