feat(02-03): create alertService with deduplication and email
- evaluateAndAlert() iterates ProbeResults and skips healthy probes - Maps 'down' -> 'service_down', 'degraded' -> 'service_degraded' - Deduplication via AlertEventModel.findRecentByService with configurable cooldown - Creates alert_events row before sending email (suppression skips both) - Recipient read from process.env.EMAIL_WEEKLY_RECIPIENT (never hardcoded) - createTransporter() called inside function scope (Firebase Secret timing fix) - Email failures caught and logged, never re-thrown
This commit is contained in:
109
backend/sql/check_table_sizes.sql
Normal file
109
backend/sql/check_table_sizes.sql
Normal file
@@ -0,0 +1,109 @@
|
||||
-- ============================================================
|
||||
-- CHECK TABLE SIZES - Run in Supabase SQL Editor
|
||||
-- ============================================================
|
||||
-- Part 1: Shows all public tables with sizes (auto-discovers)
|
||||
-- Part 2: Cleanup candidate counts (only for tables that exist)
|
||||
-- ============================================================
|
||||
|
||||
-- PART 1: All public table sizes
|
||||
SELECT
|
||||
c.relname AS table_name,
|
||||
pg_size_pretty(pg_total_relation_size(c.oid)) AS total_size,
|
||||
pg_size_pretty(pg_relation_size(c.oid)) AS data_size,
|
||||
pg_size_pretty(pg_total_relation_size(c.oid) - pg_relation_size(c.oid)) AS index_size,
|
||||
c.reltuples::bigint AS estimated_rows
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON n.oid = c.relnamespace
|
||||
WHERE n.nspname = 'public'
|
||||
AND c.relkind = 'r'
|
||||
ORDER BY pg_total_relation_size(c.oid) DESC;
|
||||
|
||||
-- PART 2: Cleanup candidates (safe — checks table existence before querying)
|
||||
DO $$
|
||||
DECLARE
|
||||
rec RECORD;
|
||||
row_count bigint;
|
||||
cleanup_count bigint;
|
||||
query text;
|
||||
BEGIN
|
||||
RAISE NOTICE '--- CLEANUP CANDIDATE BREAKDOWN ---';
|
||||
|
||||
-- Processing jobs
|
||||
IF EXISTS (SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = 'public' AND c.relname = 'processing_jobs') THEN
|
||||
SELECT count(*), count(*) FILTER (WHERE status IN ('completed', 'failed') AND completed_at < NOW() - INTERVAL '30 days')
|
||||
INTO row_count, cleanup_count FROM processing_jobs;
|
||||
RAISE NOTICE 'processing_jobs: % total, % cleanup candidates (completed/failed > 30d)', row_count, cleanup_count;
|
||||
END IF;
|
||||
|
||||
-- Vector similarity searches
|
||||
IF EXISTS (SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = 'public' AND c.relname = 'vector_similarity_searches') THEN
|
||||
SELECT count(*), count(*) FILTER (WHERE created_at < NOW() - INTERVAL '90 days')
|
||||
INTO row_count, cleanup_count FROM vector_similarity_searches;
|
||||
RAISE NOTICE 'vector_similarity_searches: % total, % cleanup candidates (> 90d)', row_count, cleanup_count;
|
||||
END IF;
|
||||
|
||||
-- Session events
|
||||
IF EXISTS (SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = 'public' AND c.relname = 'session_events') THEN
|
||||
SELECT count(*), count(*) FILTER (WHERE created_at < NOW() - INTERVAL '30 days')
|
||||
INTO row_count, cleanup_count FROM session_events;
|
||||
RAISE NOTICE 'session_events: % total, % cleanup candidates (> 30d)', row_count, cleanup_count;
|
||||
END IF;
|
||||
|
||||
-- Execution events
|
||||
IF EXISTS (SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = 'public' AND c.relname = 'execution_events') THEN
|
||||
SELECT count(*), count(*) FILTER (WHERE created_at < NOW() - INTERVAL '30 days')
|
||||
INTO row_count, cleanup_count FROM execution_events;
|
||||
RAISE NOTICE 'execution_events: % total, % cleanup candidates (> 30d)', row_count, cleanup_count;
|
||||
END IF;
|
||||
|
||||
-- Performance metrics
|
||||
IF EXISTS (SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = 'public' AND c.relname = 'performance_metrics') THEN
|
||||
SELECT count(*), count(*) FILTER (WHERE created_at < NOW() - INTERVAL '90 days')
|
||||
INTO row_count, cleanup_count FROM performance_metrics;
|
||||
RAISE NOTICE 'performance_metrics: % total, % cleanup candidates (> 90d)', row_count, cleanup_count;
|
||||
END IF;
|
||||
|
||||
-- Service health checks
|
||||
IF EXISTS (SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = 'public' AND c.relname = 'service_health_checks') THEN
|
||||
SELECT count(*), count(*) FILTER (WHERE created_at < NOW() - INTERVAL '30 days')
|
||||
INTO row_count, cleanup_count FROM service_health_checks;
|
||||
RAISE NOTICE 'service_health_checks: % total, % cleanup candidates (> 30d)', row_count, cleanup_count;
|
||||
END IF;
|
||||
|
||||
-- Alert events
|
||||
IF EXISTS (SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = 'public' AND c.relname = 'alert_events') THEN
|
||||
SELECT count(*), count(*) FILTER (WHERE created_at < NOW() - INTERVAL '30 days')
|
||||
INTO row_count, cleanup_count FROM alert_events;
|
||||
RAISE NOTICE 'alert_events: % total, % cleanup candidates (> 30d)', row_count, cleanup_count;
|
||||
END IF;
|
||||
|
||||
-- Agent executions
|
||||
IF EXISTS (SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = 'public' AND c.relname = 'agent_executions') THEN
|
||||
SELECT count(*), count(*) FILTER (WHERE created_at < NOW() - INTERVAL '90 days')
|
||||
INTO row_count, cleanup_count FROM agent_executions;
|
||||
RAISE NOTICE 'agent_executions: % total, % cleanup candidates (> 90d)', row_count, cleanup_count;
|
||||
END IF;
|
||||
|
||||
-- Agentic RAG sessions
|
||||
IF EXISTS (SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = 'public' AND c.relname = 'agentic_rag_sessions') THEN
|
||||
SELECT count(*), count(*) FILTER (WHERE created_at < NOW() - INTERVAL '90 days')
|
||||
INTO row_count, cleanup_count FROM agentic_rag_sessions;
|
||||
RAISE NOTICE 'agentic_rag_sessions: % total, % cleanup candidates (> 90d)', row_count, cleanup_count;
|
||||
END IF;
|
||||
|
||||
-- Processing quality metrics
|
||||
IF EXISTS (SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = 'public' AND c.relname = 'processing_quality_metrics') THEN
|
||||
SELECT count(*), count(*) FILTER (WHERE created_at < NOW() - INTERVAL '90 days')
|
||||
INTO row_count, cleanup_count FROM processing_quality_metrics;
|
||||
RAISE NOTICE 'processing_quality_metrics: % total, % cleanup candidates (> 90d)', row_count, cleanup_count;
|
||||
END IF;
|
||||
|
||||
-- Documents extracted_text
|
||||
IF EXISTS (SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = 'public' AND c.relname = 'documents') THEN
|
||||
SELECT count(*), count(*) FILTER (WHERE status = 'completed' AND analysis_data IS NOT NULL AND extracted_text IS NOT NULL AND created_at < NOW() - INTERVAL '30 days')
|
||||
INTO row_count, cleanup_count FROM documents;
|
||||
RAISE NOTICE 'documents (extracted_text nullable): % total, % cleanup candidates (completed > 30d with analysis_data)', row_count, cleanup_count;
|
||||
END IF;
|
||||
|
||||
RAISE NOTICE '--- END CLEANUP BREAKDOWN ---';
|
||||
END $$;
|
||||
Reference in New Issue
Block a user