/** * Analytics Refresh Handler * * Refreshes materialized views and pre-computed analytics tables. * Should run daily or on-demand after major data changes. */ import { TaskContext, TaskResult } from '../task-worker'; export async function handleAnalyticsRefresh(ctx: TaskContext): Promise { const { pool } = ctx; console.log(`[AnalyticsRefresh] Starting analytics refresh...`); const refreshed: string[] = []; const failed: string[] = []; // List of materialized views to refresh const materializedViews = [ 'mv_state_metrics', 'mv_brand_metrics', 'mv_category_metrics', 'v_brand_summary', 'v_dashboard_stats', ]; for (const viewName of materializedViews) { try { // Heartbeat before each refresh await ctx.heartbeat(); // Check if view exists const existsResult = await pool.query(` SELECT EXISTS ( SELECT 1 FROM pg_matviews WHERE matviewname = $1 UNION SELECT 1 FROM pg_views WHERE viewname = $1 ) as exists `, [viewName]); if (!existsResult.rows[0].exists) { console.log(`[AnalyticsRefresh] View ${viewName} does not exist, skipping`); continue; } // Try to refresh (only works for materialized views) try { await pool.query(`REFRESH MATERIALIZED VIEW CONCURRENTLY ${viewName}`); refreshed.push(viewName); console.log(`[AnalyticsRefresh] Refreshed ${viewName}`); } catch (refreshError: any) { // Try non-concurrent refresh try { await pool.query(`REFRESH MATERIALIZED VIEW ${viewName}`); refreshed.push(viewName); console.log(`[AnalyticsRefresh] Refreshed ${viewName} (non-concurrent)`); } catch (nonConcurrentError: any) { // Not a materialized view or other error console.log(`[AnalyticsRefresh] ${viewName} is not a materialized view or refresh failed`); } } } catch (error: any) { console.error(`[AnalyticsRefresh] Error refreshing ${viewName}:`, error.message); failed.push(viewName); } } // Run analytics capture functions if they exist const captureFunctions = [ 'capture_brand_snapshots', 'capture_category_snapshots', ]; for (const funcName of captureFunctions) { try { await pool.query(`SELECT ${funcName}()`); console.log(`[AnalyticsRefresh] Executed ${funcName}()`); } catch (error: any) { // Function might not exist console.log(`[AnalyticsRefresh] ${funcName}() not available`); } } console.log(`[AnalyticsRefresh] Complete: ${refreshed.length} refreshed, ${failed.length} failed`); return { success: failed.length === 0, refreshed, failed, error: failed.length > 0 ? `Failed to refresh: ${failed.join(', ')}` : undefined, }; }