-- Create diagnostic queries tableCREATETABLEdiagnostic_queries(idSERIALPRIMARYKEY,query_nameVARCHAR(100)UNIQUENOTNULL,query_sqlTEXTNOTNULL,descriptionTEXT,categoryVARCHAR(50)NOTNULL,severityVARCHAR(20)DEFAULT'info',created_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP);-- Insert common diagnostic queriesINSERTINTOdiagnostic_queries(query_name,query_sql,description,category,severity)VALUES('active_connections','SELECT COUNT(*) as active_connections FROM pg_stat_activity WHERE state = ''active''','Check number of active connections','connections','warning'),('long_running_queries','SELECT pid, now() - pg_stat_activity.query_start AS duration, query FROM pg_stat_activity WHERE (now() - pg_stat_activity.query_start) > interval ''5 minutes''','Find long-running queries','performance','critical'),('database_size','SELECT pg_size_pretty(pg_database_size(current_database())) as database_size','Check database size','storage','info'),('table_bloat','SELECT schemaname, tablename, pg_size_pretty(pg_total_relation_size(schemaname||''.''||tablename)) as size FROM pg_tables ORDER BY pg_total_relation_size(schemaname||''.''||tablename) DESC LIMIT 10','Find largest tables','storage','info');-- Create function to run diagnostic queryCREATEORREPLACEFUNCTIONrun_diagnostic_query(p_query_nameVARCHAR(100))RETURNSTABLE(result_dataJSONB)AS$$DECLAREquery_sqlTEXT;BEGINSELECTdq.query_sqlINTOquery_sqlFROMdiagnostic_queriesdqWHEREdq.query_name=p_query_name;IFquery_sqlISNULLTHENRAISEEXCEPTION'Diagnostic query % not found',p_query_name;ENDIF;RETURNQUERYEXECUTEquery_sql;END;$$LANGUAGEplpgsql;
-- Create function to analyze slow queriesCREATEORREPLACEFUNCTIONanalyze_slow_queries(p_duration_thresholdINTERVALDEFAULT'1 minute')RETURNSTABLE(pidINTEGER,durationINTERVAL,queryTEXT,stateVARCHAR(20),wait_event_typeVARCHAR(50),wait_eventVARCHAR(50))AS$$BEGINRETURNQUERYSELECTa.pid,now()-a.query_startasduration,a.query,a.state,a.wait_event_type,a.wait_eventFROMpg_stat_activityaWHEREa.query_startISNOTNULLANDnow()-a.query_start>p_duration_thresholdANDa.state!='idle'ORDERBYdurationDESC;END;$$LANGUAGEplpgsql;-- Create function to check lock contentionCREATEORREPLACEFUNCTIONcheck_lock_contention()RETURNSTABLE(blocked_pidINTEGER,blocked_queryTEXT,blocking_pidINTEGER,blocking_queryTEXT,lock_typeVARCHAR(50),modeVARCHAR(20),grantedBOOLEAN)AS$$BEGINRETURNQUERYSELECTblocked_locks.pidASblocked_pid,blocked_activity.queryASblocked_query,blocking_locks.pidASblocking_pid,blocking_activity.queryASblocking_query,blocked_locks.locktypeASlock_type,blocked_locks.modeASmode,blocked_locks.grantedASgrantedFROMpg_catalog.pg_locksblocked_locksJOINpg_catalog.pg_stat_activityblocked_activityONblocked_activity.pid=blocked_locks.pidJOINpg_catalog.pg_locksblocking_locksONblocking_locks.locktype=blocked_locks.locktypeANDblocking_locks.databaseISNOTDISTINCTFROMblocked_locks.databaseANDblocking_locks.relationISNOTDISTINCTFROMblocked_locks.relationANDblocking_locks.pageISNOTDISTINCTFROMblocked_locks.pageANDblocking_locks.tupleISNOTDISTINCTFROMblocked_locks.tupleANDblocking_locks.virtualxidISNOTDISTINCTFROMblocked_locks.virtualxidANDblocking_locks.transactionidISNOTDISTINCTFROMblocked_locks.transactionidANDblocking_locks.classidISNOTDISTINCTFROMblocked_locks.classidANDblocking_locks.objidISNOTDISTINCTFROMblocked_locks.objidANDblocking_locks.objsubidISNOTDISTINCTFROMblocked_locks.objsubidANDblocking_locks.pid!=blocked_locks.pidJOINpg_catalog.pg_stat_activityblocking_activityONblocking_activity.pid=blocking_locks.pidWHERENOTblocked_locks.granted;END;$$LANGUAGEplpgsql;
-- Create function to diagnose connection issuesCREATEORREPLACEFUNCTIONdiagnose_connection_issues()RETURNSTABLE(issue_typeVARCHAR(50),descriptionTEXT,current_valueTEXT,recommended_valueTEXT,severityVARCHAR(20))AS$$BEGIN-- Check max_connectionsRETURNQUERYSELECT'max_connections'::VARCHAR(50),'Maximum number of concurrent connections'::TEXT,current_setting('max_connections')::TEXT,'200'::TEXT,CASEWHENcurrent_setting('max_connections')::INTEGER<100THEN'critical'WHENcurrent_setting('max_connections')::INTEGER<200THEN'warning'ELSE'info'END::VARCHAR(20);-- Check active connectionsRETURNQUERYSELECT'active_connections'::VARCHAR(50),'Current number of active connections'::TEXT,(SELECTCOUNT(*)::TEXTFROMpg_stat_activity)::TEXT,'Less than 80% of max_connections'::TEXT,CASEWHEN(SELECTCOUNT(*)FROMpg_stat_activity)::FLOAT/current_setting('max_connections')::FLOAT>0.8THEN'critical'WHEN(SELECTCOUNT(*)FROMpg_stat_activity)::FLOAT/current_setting('max_connections')::FLOAT>0.6THEN'warning'ELSE'info'END::VARCHAR(20);-- Check connection timeoutRETURNQUERYSELECT'idle_in_transaction_session_timeout'::VARCHAR(50),'Timeout for idle transactions'::TEXT,current_setting('idle_in_transaction_session_timeout')::TEXT,'300000'::TEXT,CASEWHENcurrent_setting('idle_in_transaction_session_timeout')::INTEGER=0THEN'warning'ELSE'info'END::VARCHAR(20);END;$$LANGUAGEplpgsql;
-- Create function to diagnose performance issuesCREATEORREPLACEFUNCTIONdiagnose_performance_issues()RETURNSTABLE(issue_typeVARCHAR(50),descriptionTEXT,current_valueTEXT,recommended_valueTEXT,severityVARCHAR(20))AS$$BEGIN-- Check shared_buffersRETURNQUERYSELECT'shared_buffers'::VARCHAR(50),'Shared buffer pool size'::TEXT,current_setting('shared_buffers')::TEXT,'25% of total RAM'::TEXT,CASEWHENcurrent_setting('shared_buffers')::INTEGER<128THEN'critical'WHENcurrent_setting('shared_buffers')::INTEGER<256THEN'warning'ELSE'info'END::VARCHAR(20);-- Check work_memRETURNQUERYSELECT'work_mem'::VARCHAR(50),'Memory for sorting and hashing'::TEXT,current_setting('work_mem')::TEXT,'4MB'::TEXT,CASEWHENcurrent_setting('work_mem')::INTEGER<1024THEN'critical'WHENcurrent_setting('work_mem')::INTEGER<4096THEN'warning'ELSE'info'END::VARCHAR(20);-- Check effective_cache_sizeRETURNQUERYSELECT'effective_cache_size'::VARCHAR(50),'Estimated cache size for query planning'::TEXT,current_setting('effective_cache_size')::TEXT,'75% of total RAM'::TEXT,CASEWHENcurrent_setting('effective_cache_size')::INTEGER<1024THEN'critical'WHENcurrent_setting('effective_cache_size')::INTEGER<2048THEN'warning'ELSE'info'END::VARCHAR(20);END;$$LANGUAGEplpgsql;
-- Create function to detect error patternsCREATEORREPLACEFUNCTIONdetect_error_patterns(p_hoursINTEGERDEFAULT24)RETURNSTABLE(error_patternVARCHAR(100),error_countBIGINT,first_occurrenceTIMESTAMPTZ,last_occurrenceTIMESTAMPTZ,severityVARCHAR(20))AS$$BEGINRETURNQUERYSELECTCASEWHENla.log_messageILIKE'%connection%'THEN'connection_errors'WHENla.log_messageILIKE'%timeout%'THEN'timeout_errors'WHENla.log_messageILIKE'%deadlock%'THEN'deadlock_errors'WHENla.log_messageILIKE'%constraint%'THEN'constraint_errors'WHENla.log_messageILIKE'%permission%'THEN'permission_errors'ELSE'other_errors'ENDaserror_pattern,COUNT(*)aserror_count,MIN(la.timestamp)asfirst_occurrence,MAX(la.timestamp)aslast_occurrence,CASEWHENCOUNT(*)>100THEN'critical'WHENCOUNT(*)>50THEN'warning'ELSE'info'ENDasseverityFROMlog_analysislaWHEREla.log_levelIN('ERROR','FATAL','PANIC')ANDla.timestamp>CURRENT_TIMESTAMP-(p_hours||' hours')::intervalGROUPBYerror_patternORDERBYerror_countDESC;END;$$LANGUAGEplpgsql;
# troubleshooting/postgres_troubleshooter.pyimportpsycopg2importjsonimportrefromdatetimeimportdatetime,timedeltaimportloggingclassPostgreSQLTroubleshooter:def__init__(self,connection_params):self.conn_params=connection_paramsself.setup_logging()defsetup_logging(self):"""Setup logging."""logging.basicConfig(level=logging.INFO)self.logger=logging.getLogger(__name__)defrun_health_checks(self):"""Run comprehensive health checks."""conn=psycopg2.connect(**self.conn_params)try:withconn.cursor()ascur:# Check active connectionscur.execute("SELECT COUNT(*) FROM pg_stat_activity WHERE state = 'active'")active_connections=cur.fetchone()[0]# Check long-running queriescur.execute(""" SELECT COUNT(*) FROM pg_stat_activity WHERE state = 'active' AND now() - query_start > interval '5 minutes' """)long_queries=cur.fetchone()[0]# Check lock contentioncur.execute("SELECT COUNT(*) FROM pg_locks WHERE NOT granted")lock_contention=cur.fetchone()[0]# Check database sizecur.execute("SELECT pg_database_size(current_database())")db_size=cur.fetchone()[0]return{'active_connections':active_connections,'long_queries':long_queries,'lock_contention':lock_contention,'database_size':db_size}exceptExceptionase:self.logger.error(f"Error running health checks: {e}")return{}finally:conn.close()defdiagnose_performance_issues(self):"""Diagnose performance issues."""conn=psycopg2.connect(**self.conn_params)try:withconn.cursor()ascur:# Get slow queriescur.execute(""" SELECT pid, now() - query_start as duration, query FROM pg_stat_activity WHERE state = 'active' AND now() - query_start > interval '1 minute' ORDER BY duration DESC """)slow_queries=cur.fetchall()# Get lock informationcur.execute(""" SELECT blocked_locks.pid as blocked_pid, blocked_activity.query as blocked_query, blocking_locks.pid as blocking_pid, blocking_activity.query as blocking_query FROM pg_catalog.pg_locks blocked_locks JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid JOIN pg_catalog.pg_locks blocking_locks ON blocking_locks.locktype = blocked_locks.locktype WHERE NOT blocked_locks.granted """)lock_info=cur.fetchall()return{'slow_queries':slow_queries,'lock_info':lock_info}exceptExceptionase:self.logger.error(f"Error diagnosing performance issues: {e}")return{}finally:conn.close()defanalyze_logs(self,log_file_path):"""Analyze PostgreSQL logs."""error_patterns={'connection_errors':r'connection.*refused|connection.*reset','timeout_errors':r'timeout|timed out','deadlock_errors':r'deadlock|deadlock detected','constraint_errors':r'constraint.*violation','permission_errors':r'permission.*denied|access.*denied'}error_counts={}try:withopen(log_file_path,'r')asf:forlineinf:forpattern_name,patterninerror_patterns.items():ifre.search(pattern,line,re.IGNORECASE):error_counts[pattern_name]=error_counts.get(pattern_name,0)+1returnerror_countsexceptExceptionase:self.logger.error(f"Error analyzing logs: {e}")return{}defgenerate_troubleshooting_report(self):"""Generate comprehensive troubleshooting report."""health_checks=self.run_health_checks()performance_issues=self.diagnose_performance_issues()report={'report_timestamp':datetime.now().isoformat(),'health_checks':health_checks,'performance_issues':performance_issues,'recommendations':self.generate_recommendations(health_checks,performance_issues)}returnreportdefgenerate_recommendations(self,health_checks,performance_issues):"""Generate troubleshooting recommendations."""recommendations=[]# Connection recommendationsifhealth_checks.get('active_connections',0)>100:recommendations.append({'issue':'High connection count','recommendation':'Consider connection pooling or increasing max_connections','severity':'warning'})# Performance recommendationsifperformance_issues.get('slow_queries'):recommendations.append({'issue':'Slow queries detected','recommendation':'Review and optimize slow queries, consider adding indexes','severity':'critical'})ifperformance_issues.get('lock_info'):recommendations.append({'issue':'Lock contention detected','recommendation':'Review transaction isolation levels and query patterns','severity':'warning'})returnrecommendations# Usageif__name__=="__main__":troubleshooter=PostgreSQLTroubleshooter({'host':'localhost','database':'production','user':'troubleshooter_user','password':'troubleshooter_password'})report=troubleshooter.generate_troubleshooting_report()print(json.dumps(report,indent=2))
-- Create emergency procedures tableCREATETABLEemergency_procedures(idSERIALPRIMARYKEY,procedure_nameVARCHAR(100)UNIQUENOTNULL,descriptionTEXTNOTNULL,sql_commandTEXTNOTNULL,risk_levelVARCHAR(20)NOTNULL,created_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP);-- Insert emergency proceduresINSERTINTOemergency_procedures(procedure_name,description,sql_command,risk_level)VALUES('kill_long_queries','Kill long-running queries','SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = ''active'' AND now() - query_start > interval ''10 minutes''','high'),('kill_idle_connections','Kill idle connections','SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = ''idle'' AND now() - state_change > interval ''1 hour''','medium'),('force_vacuum','Force VACUUM on all tables','VACUUM ANALYZE','low'),('check_disk_space','Check disk space usage','SELECT pg_size_pretty(pg_database_size(current_database()))','low');-- Create function to execute emergency procedureCREATEORREPLACEFUNCTIONexecute_emergency_procedure(p_procedure_nameVARCHAR(100))RETURNSTEXTAS$$DECLAREprocedure_sqlTEXT;result_textTEXT;BEGINSELECTsql_commandINTOprocedure_sqlFROMemergency_proceduresWHEREprocedure_name=p_procedure_name;IFprocedure_sqlISNULLTHENRAISEEXCEPTION'Emergency procedure % not found',p_procedure_name;ENDIF;EXECUTEprocedure_sql;RETURN'Emergency procedure '||p_procedure_name||' executed successfully';END;$$LANGUAGEplpgsql;
-- 1. Run health checksSELECT*FROMdiagnose_connection_issues();SELECT*FROMdiagnose_performance_issues();-- 2. Check slow queriesSELECT*FROManalyze_slow_queries('1 minute');-- 3. Check lock contentionSELECT*FROMcheck_lock_contention();-- 4. Run emergency procedures if neededSELECTexecute_emergency_procedure('kill_long_queries');