-- Database Performance Dashboard -- Shows aggregated database query metrics grouped by query pattern WITH query_classification AS ( -- Classify queries by type and extract table names from actual SQL SELECT span_name, attributes->>'db.statement' as query_text, duration, start_timestamp, trace_id, otel_status_code, CASE WHEN span_name LIKE 'SELECT%' THEN 'SELECT' WHEN span_name LIKE 'INSERT%' THEN 'INSERT' WHEN span_name LIKE 'UPDATE%' THEN 'UPDATE' WHEN span_name LIKE 'DELETE%' THEN 'DELETE' WHEN span_name LIKE 'BEGIN%' THEN 'TRANSACTION' WHEN span_name LIKE 'COMMIT%' THEN 'TRANSACTION' WHEN span_name LIKE 'ROLLBACK%' THEN 'TRANSACTION' ELSE 'OTHER' END AS query_type, -- Extract primary table from actual SQL query in attributes CASE -- tracks queries (most common) WHEN attributes->>'db.statement' LIKE '%FROM tracks%' AND attributes->>'db.statement' NOT LIKE '%JOIN%' THEN 'tracks' WHEN attributes->>'db.statement' LIKE '%FROM tracks JOIN%' THEN 'tracks+joins' WHEN attributes->>'db.statement' LIKE '%INTO tracks%' THEN 'tracks' WHEN attributes->>'db.statement' LIKE '%UPDATE tracks%' THEN 'tracks' -- track_likes queries WHEN attributes->>'db.statement' LIKE '%FROM track_likes%' THEN 'track_likes' WHEN attributes->>'db.statement' LIKE '%INTO track_likes%' THEN 'track_likes' -- user auth/session queries WHEN attributes->>'db.statement' LIKE '%FROM user_sessions%' THEN 'user_sessions' WHEN attributes->>'db.statement' LIKE '%INTO user_sessions%' THEN 'user_sessions' WHEN attributes->>'db.statement' LIKE '%UPDATE user_sessions%' THEN 'user_sessions' -- user preferences WHEN attributes->>'db.statement' LIKE '%FROM user_preferences%' THEN 'user_preferences' WHEN attributes->>'db.statement' LIKE '%INTO user_preferences%' THEN 'user_preferences' WHEN attributes->>'db.statement' LIKE '%UPDATE user_preferences%' THEN 'user_preferences' -- queue state WHEN attributes->>'db.statement' LIKE '%FROM queue_state%' THEN 'queue_state' WHEN attributes->>'db.statement' LIKE '%INTO queue_state%' THEN 'queue_state' WHEN attributes->>'db.statement' LIKE '%UPDATE queue_state%' THEN 'queue_state' -- artists WHEN attributes->>'db.statement' LIKE '%FROM artists%' THEN 'artists' WHEN attributes->>'db.statement' LIKE '%INTO artists%' THEN 'artists' WHEN attributes->>'db.statement' LIKE '%UPDATE artists%' THEN 'artists' -- albums WHEN attributes->>'db.statement' LIKE '%FROM albums%' THEN 'albums' WHEN attributes->>'db.statement' LIKE '%INTO albums%' THEN 'albums' ELSE 'other/unknown' END AS primary_table FROM records WHERE -- Filter for database operations (span_name = 'SELECT neondb' OR span_name = 'INSERT neondb' OR span_name = 'UPDATE neondb' OR span_name = 'DELETE neondb' OR span_name = 'BEGIN neondb' OR span_name = 'COMMIT neondb' OR span_name = 'ROLLBACK neondb') -- Exclude very fast queries (connection pool pings, etc.) AND duration > 0.001 ), aggregated_metrics AS ( -- Aggregate by query type and table SELECT query_type, primary_table, COUNT(*) AS total_queries, ROUND(AVG(duration * 1000)::numeric, 2) AS avg_duration_ms, ROUND(PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY duration * 1000)::numeric, 2) AS p50_duration_ms, ROUND(PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY duration * 1000)::numeric, 2) AS p95_duration_ms, ROUND(PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY duration * 1000)::numeric, 2) AS p99_duration_ms, ROUND(MAX(duration * 1000)::numeric, 2) AS max_duration_ms, COUNT(*) FILTER (WHERE otel_status_code = 'ERROR') AS error_count, ROUND((COUNT(*) FILTER (WHERE otel_status_code = 'ERROR')::numeric / COUNT(*)::numeric * 100), 2) AS error_rate_pct FROM query_classification GROUP BY query_type, primary_table ) SELECT query_type AS "Query Type", primary_table AS "Table", total_queries AS "Count", avg_duration_ms AS "Avg (ms)", p50_duration_ms AS "p50 (ms)", p95_duration_ms AS "p95 (ms)", p99_duration_ms AS "p99 (ms)", max_duration_ms AS "Max (ms)", error_count AS "Errors", COALESCE(error_rate_pct, 0) AS "Error Rate %" FROM aggregated_metrics WHERE total_queries > 0 ORDER BY -- Sort by most impactful queries first (total_queries * avg_duration_ms) DESC, error_count DESC LIMIT 50; -- Alternative: Top Slowest Individual Queries -- Uncomment to see the actual slowest query instances instead of aggregated patterns /* SELECT ROUND(duration * 1000, 2) AS "Duration (ms)", CASE WHEN span_name = 'SELECT neondb' THEN 'SELECT' WHEN span_name = 'INSERT neondb' THEN 'INSERT' WHEN span_name = 'UPDATE neondb' THEN 'UPDATE' WHEN span_name = 'DELETE neondb' THEN 'DELETE' ELSE 'OTHER' END AS "Type", LEFT(attributes->>'db.statement', 150) AS "Query Preview", start_timestamp AS "Timestamp", trace_id AS "Trace ID", otel_status_code AS "Status" FROM records WHERE (span_name = 'SELECT neondb' OR span_name = 'INSERT neondb' OR span_name = 'UPDATE neondb' OR span_name = 'DELETE neondb') AND duration > 0.001 ORDER BY duration DESC LIMIT 25; */ -- Alternative: Database Operations Timeline (5-minute aggregation) -- Uncomment to see query volume and performance over time /* WITH time_metrics AS ( SELECT DATE_TRUNC('minute', start_timestamp) AS minute, CASE WHEN span_name = 'SELECT neondb' THEN 'SELECT' WHEN span_name = 'INSERT neondb' THEN 'INSERT' WHEN span_name = 'UPDATE neondb' THEN 'UPDATE' WHEN span_name = 'DELETE neondb' THEN 'DELETE' ELSE 'OTHER' END AS query_type, COUNT(*) AS query_count, ROUND(AVG(duration * 1000)::numeric, 2) AS avg_duration_ms, COUNT(*) FILTER (WHERE otel_status_code = 'ERROR') AS error_count FROM records WHERE (span_name = 'SELECT neondb' OR span_name = 'INSERT neondb' OR span_name = 'UPDATE neondb' OR span_name = 'DELETE neondb') AND start_timestamp > NOW() - INTERVAL '2 hours' GROUP BY minute, query_type ) SELECT minute AS "Time", query_type AS "Type", query_count AS "Count", avg_duration_ms AS "Avg Duration (ms)", error_count AS "Errors" FROM time_metrics ORDER BY minute DESC, query_count DESC; */ -- Alternative: Connection Pool and Transaction Metrics -- Uncomment to analyze transaction patterns and connection behavior /* WITH transaction_metrics AS ( SELECT DATE_TRUNC('minute', start_timestamp) AS minute, COUNT(*) FILTER (WHERE span_name = 'BEGIN neondb') AS begin_count, COUNT(*) FILTER (WHERE span_name = 'COMMIT neondb') AS commit_count, COUNT(*) FILTER (WHERE span_name = 'ROLLBACK neondb') AS rollback_count, ROUND(AVG(duration * 1000) FILTER (WHERE span_name = 'BEGIN neondb')::numeric, 2) AS avg_begin_ms, ROUND(AVG(duration * 1000) FILTER (WHERE span_name = 'COMMIT neondb')::numeric, 2) AS avg_commit_ms FROM records WHERE span_name = 'BEGIN neondb' OR span_name = 'COMMIT neondb' OR span_name = 'ROLLBACK neondb' GROUP BY minute ) SELECT minute AS "Minute", begin_count AS "Begins", commit_count AS "Commits", rollback_count AS "Rollbacks", avg_begin_ms AS "Avg BEGIN (ms)", avg_commit_ms AS "Avg COMMIT (ms)", CASE WHEN begin_count > 0 THEN ROUND((rollback_count::numeric / begin_count::numeric * 100), 2) ELSE 0 END AS "Rollback Rate %" FROM transaction_metrics WHERE begin_count > 0 OR commit_count > 0 OR rollback_count > 0 ORDER BY minute DESC LIMIT 60; */