Maintenance
Maintenance
Section titled “Maintenance”Routine maintenance tasks to keep your Mantis database healthy and performant.
Routine Maintenance
Section titled “Routine Maintenance”Autovacuum
Section titled “Autovacuum”PostgreSQL autovacuum handles most maintenance automatically:
# postgresql.confautovacuum = onautovacuum_max_workers = 3autovacuum_naptime = 60autovacuum_vacuum_threshold = 50autovacuum_analyze_threshold = 50autovacuum_vacuum_scale_factor = 0.2autovacuum_analyze_scale_factor = 0.1Monitor Autovacuum
Section titled “Monitor Autovacuum”-- Check autovacuum activitySELECT relname, last_vacuum, last_autovacuum, last_analyze, last_autoanalyzeFROM pg_stat_user_tablesORDER BY last_autovacuum DESC NULLS LAST;
-- Tables needing vacuumSELECT schemaname, relname, n_dead_tup, n_live_tup, round(n_dead_tup * 100.0 / NULLIF(n_live_tup, 0), 2) as dead_pctFROM pg_stat_user_tablesWHERE n_dead_tup > 1000ORDER BY n_dead_tup DESC;Manual Vacuum
Section titled “Manual Vacuum”For immediate maintenance:
-- Vacuum specific tableVACUUM (VERBOSE) deployment_history;
-- Vacuum and analyzeVACUUM ANALYZE deployment_history;
-- Full vacuum (requires exclusive lock)VACUUM FULL deployment_history;Index Maintenance
Section titled “Index Maintenance”Check Index Health
Section titled “Check Index Health”-- Index usage statisticsSELECT schemaname, tablename, indexname, idx_scan, idx_tup_read, idx_tup_fetchFROM pg_stat_user_indexesORDER BY idx_scan DESC;
-- Unused indexes (candidates for removal)SELECT schemaname, tablename, indexname, idx_scanFROM pg_stat_user_indexesWHERE idx_scan = 0AND indexrelname NOT LIKE 'pk_%'ORDER BY schemaname, tablename;
-- Index sizesSELECT indexrelname as index_name, pg_size_pretty(pg_relation_size(indexrelid)) as index_sizeFROM pg_stat_user_indexesORDER BY pg_relation_size(indexrelid) DESC;Reindex
Section titled “Reindex”Rebuild corrupted or bloated indexes:
-- Reindex single indexREINDEX INDEX idx_dh_status;
-- Reindex tableREINDEX TABLE deployment_history;
-- Reindex concurrently (PostgreSQL 12+)REINDEX INDEX CONCURRENTLY idx_dh_status;Index Bloat Detection
Section titled “Index Bloat Detection”-- Check for bloated indexesSELECT current_database() AS db, schemaname, tablename, indexrelname AS index_name, pg_size_pretty(index_size) AS index_size, pg_size_pretty(index_size - expected_size) AS bloat, round((index_size - expected_size) * 100.0 / index_size, 2) AS bloat_pctFROM ( SELECT schemaname, tablename, indexrelname, pg_relation_size(indexrelid) AS index_size, (avg_leaf_density / 90.0) * pg_relation_size(indexrelid) AS expected_size FROM pg_stat_user_indexes JOIN pg_index USING (indexrelid) JOIN pg_class ON indexrelid = pg_class.oid CROSS JOIN LATERAL ( SELECT (100.0 - COALESCE(avg_leaf_density, 90.0)) AS avg_leaf_density FROM pg_stats WHERE tablename = pg_class.relname LIMIT 1 ) s WHERE pg_relation_size(indexrelid) > 10485760 -- > 10MB) tWHERE index_size > expected_size * 1.3 -- > 30% bloatORDER BY bloat DESC;Table Maintenance
Section titled “Table Maintenance”Check Table Statistics
Section titled “Check Table Statistics”-- Table sizesSELECT relname, pg_size_pretty(pg_total_relation_size(relid)) as total_size, pg_size_pretty(pg_relation_size(relid)) as table_size, pg_size_pretty(pg_indexes_size(relid)) as index_sizeFROM pg_stat_user_tablesORDER BY pg_total_relation_size(relid) DESC;
-- Row countsSELECT relname, n_live_tup as row_countFROM pg_stat_user_tablesORDER BY n_live_tup DESC;Table Bloat
Section titled “Table Bloat”-- Estimate table bloatSELECT schemaname, tablename, pg_size_pretty(pg_total_relation_size(schemaname || '.' || tablename)) as total_size, pg_size_pretty( pg_total_relation_size(schemaname || '.' || tablename) - pg_relation_size(schemaname || '.' || tablename) ) as bloat_estimateFROM pg_stat_user_tablesORDER BY pg_total_relation_size(schemaname || '.' || tablename) DESC;Reduce Bloat
Section titled “Reduce Bloat”-- Option 1: VACUUM FULL (locks table)VACUUM FULL tablename;
-- Option 2: pg_repack (online, no locks)-- Install extension firstCREATE EXTENSION pg_repack;
-- Repack tableSELECT pg_repack.repack_table('public.deployment_history');Monitoring Queries
Section titled “Monitoring Queries”Slow Queries
Section titled “Slow Queries”-- Enable query loggingALTER SYSTEM SET log_min_duration_statement = 1000; -- 1 secondSELECT pg_reload_conf();
-- Find slow queriesSELECT query, calls, mean_time, total_timeFROM pg_stat_statementsORDER BY mean_time DESCLIMIT 20;Lock Monitoring
Section titled “Lock Monitoring”-- Current locksSELECT pid, relation::regclass, mode, grantedFROM pg_locksWHERE relation IS NOT NULLORDER BY relation;
-- Blocked queriesSELECT blocked.pid AS blocked_pid, blocked.query AS blocked_query, blocking.pid AS blocking_pid, blocking.query AS blocking_queryFROM pg_stat_activity blockedJOIN pg_locks blocked_locks ON blocked.pid = blocked_locks.pidJOIN pg_locks blocking_locks ON blocked_locks.locktype = blocking_locks.locktype AND blocked_locks.relation = blocking_locks.relation AND blocked_locks.pid != blocking_locks.pidJOIN pg_stat_activity blocking ON blocking_locks.pid = blocking.pidWHERE NOT blocked_locks.granted;Connection Monitoring
Section titled “Connection Monitoring”-- Connection summarySELECT state, count(*)FROM pg_stat_activityWHERE datname = 'mantis'GROUP BY state;
-- Active queriesSELECT pid, usename, application_name, now() - query_start as query_duration, state, queryFROM pg_stat_activityWHERE datname = 'mantis'AND state = 'active'ORDER BY query_start;Scheduled Maintenance
Section titled “Scheduled Maintenance”Daily Tasks
Section titled “Daily Tasks”Create maintenance script:
#!/bin/bash# Analyze tablespsql -U mantis -d mantis -c "ANALYZE;"
# Check for bloatpsql -U mantis -d mantis -c "SELECT relname, n_dead_tupFROM pg_stat_user_tablesWHERE n_dead_tup > 10000ORDER BY n_dead_tup DESC;"Weekly Tasks
Section titled “Weekly Tasks”#!/bin/bash# Reindex concurrentlypsql -U mantis -d mantis -c "REINDEX INDEX CONCURRENTLY idx_dh_status;REINDEX INDEX CONCURRENTLY idx_execution_steps_deployment_id;"
# Vacuum verbosepsql -U mantis -d mantis -c "VACUUM VERBOSE;"Schedule with Cron
Section titled “Schedule with Cron”# Daily at 3 AM0 3 * * * mantis /opt/mantis/scripts/daily-maintenance.sh >> /var/log/mantis/maintenance.log 2>&1
# Weekly on Sunday at 4 AM0 4 * * 0 mantis /opt/mantis/scripts/weekly-maintenance.sh >> /var/log/mantis/maintenance.log 2>&1Data Retention
Section titled “Data Retention”Deployment History
Section titled “Deployment History”Clean up old deployment data:
-- Delete deployment data older than 90 daysDELETE FROM deployment_logsWHERE deployment_id IN ( SELECT id FROM deployment_history WHERE created_at < NOW() - INTERVAL '90 days');
DELETE FROM execution_stepsWHERE deployment_id IN ( SELECT id FROM deployment_history WHERE created_at < NOW() - INTERVAL '90 days');
DELETE FROM deployment_historyWHERE created_at < NOW() - INTERVAL '90 days';
-- Vacuum after large deletesVACUUM ANALYZE deployment_history, execution_steps, deployment_logs;Audit Logs
Section titled “Audit Logs”-- Archive old audit logsCREATE TABLE audit_log_entries_archive ASSELECT * FROM audit_log_entriesWHERE occurred_at < NOW() - INTERVAL '365 days';
DELETE FROM audit_log_entriesWHERE occurred_at < NOW() - INTERVAL '365 days';
VACUUM ANALYZE audit_log_entries;Automated Retention Script
Section titled “Automated Retention Script”#!/bin/bashRETENTION_DAYS=${RETENTION_DAYS:-90}
psql -U mantis -d mantis <<EOFBEGIN;
-- Delete old deployment dataDELETE FROM deployment_logsWHERE deployment_id IN ( SELECT id FROM deployment_history WHERE created_at < NOW() - INTERVAL '${RETENTION_DAYS} days');
DELETE FROM execution_stepsWHERE deployment_id IN ( SELECT id FROM deployment_history WHERE created_at < NOW() - INTERVAL '${RETENTION_DAYS} days');
DELETE FROM deployment_historyWHERE created_at < NOW() - INTERVAL '${RETENTION_DAYS} days';
COMMIT;
VACUUM ANALYZE;EOFPerformance Tuning
Section titled “Performance Tuning”Query Optimization
Section titled “Query Optimization”-- Analyze query planEXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)SELECT * FROM deployment_historyWHERE tenant_id = '...' AND status = 'success'ORDER BY created_at DESCLIMIT 50;
-- Check for sequential scansSELECT relname, seq_scan, seq_tup_read, idx_scan, idx_tup_fetchFROM pg_stat_user_tablesWHERE seq_scan > 0ORDER BY seq_tup_read DESC;Missing Indexes
Section titled “Missing Indexes”-- Find queries that might need indexesSELECT schemaname, tablename, seq_scan, seq_tup_read, idx_scan, idx_tup_fetch, seq_tup_read / NULLIF(seq_scan, 0) as avg_seq_tupFROM pg_stat_user_tablesWHERE seq_scan > 100AND seq_tup_read / NULLIF(seq_scan, 0) > 1000ORDER BY seq_tup_read DESC;Statistics Update
Section titled “Statistics Update”-- Update table statisticsANALYZE deployment_history;
-- Increase statistics target for frequently queried columnsALTER TABLE deployment_history ALTER COLUMN status SET STATISTICS 500;ANALYZE deployment_history;Health Checks
Section titled “Health Checks”Automated Health Check Script
Section titled “Automated Health Check Script”#!/bin/bash# Connection testif ! psql -U mantis -d mantis -c "SELECT 1" > /dev/null 2>&1; then echo "ERROR: Cannot connect to database" exit 1fi
# Check for long-running queriesLONG_QUERIES=$(psql -U mantis -d mantis -t -c "SELECT count(*) FROM pg_stat_activityWHERE state = 'active'AND now() - query_start > interval '5 minutes'")
if [ "$LONG_QUERIES" -gt 0 ]; then echo "WARNING: $LONG_QUERIES long-running queries detected"fi
# Check for high bloatBLOATED_TABLES=$(psql -U mantis -d mantis -t -c "SELECT count(*) FROM pg_stat_user_tablesWHERE n_dead_tup > 100000")
if [ "$BLOATED_TABLES" -gt 0 ]; then echo "WARNING: $BLOATED_TABLES tables with high dead tuple count"fi
# Check connection countCONNECTIONS=$(psql -U mantis -d mantis -t -c "SELECT count(*) FROM pg_stat_activity WHERE datname = 'mantis'")MAX_CONN=$(psql -U mantis -d mantis -t -c "SHOW max_connections")
if [ "$CONNECTIONS" -gt $((MAX_CONN * 80 / 100)) ]; then echo "WARNING: Connection usage above 80% ($CONNECTIONS/$MAX_CONN)"fi
echo "Database health check completed"Next Steps
Section titled “Next Steps”- PostgreSQL Setup - Initial setup
- Connection Pooling - Pool management
- Backup & Recovery - Backup procedures
