Files
yourpart3/backend/vacuum-database.js

114 lines
4.0 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* Script zum Ausführen von VACUUM auf Tabellen mit hohem Bloat
*
* Führt VACUUM ANALYZE auf Tabellen aus, die:
* - Hohen Bloat-Anteil haben (> 20% tote Zeilen)
* - Seit mehr als 7 Tagen nicht gevacuumt wurden
* - Viele tote Zeilen haben (> 1000)
*/
import './config/loadEnv.js';
import { sequelize } from './utils/sequelize.js';
async function main() {
try {
console.log('🧹 Datenbank-Vacuum\n');
console.log('='.repeat(60) + '\n');
// Finde Tabellen, die Vacuum benötigen
const [tablesToVacuum] = await sequelize.query(`
SELECT
schemaname || '.' || relname as table_name,
n_dead_tup,
n_live_tup,
CASE
WHEN n_live_tup > 0 THEN round((n_dead_tup::numeric / n_live_tup::numeric) * 100, 2)
ELSE 0
END as dead_percent,
last_vacuum,
last_autovacuum,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||relname)) as table_size
FROM pg_stat_user_tables
WHERE schemaname IN ('falukant_data', 'falukant_type', 'community', 'logs')
AND (
(n_dead_tup > 1000 AND (n_live_tup = 0 OR (n_dead_tup::numeric / NULLIF(n_live_tup, 0)) > 0.2))
OR (last_vacuum IS NULL AND last_autovacuum IS NULL AND n_dead_tup > 0)
OR (
(last_vacuum < now() - interval '7 days' OR last_vacuum IS NULL)
AND (last_autovacuum < now() - interval '7 days' OR last_autovacuum IS NULL)
AND n_dead_tup > 100
)
)
ORDER BY n_dead_tup DESC;
`);
if (tablesToVacuum.length === 0) {
console.log('✅ Keine Tabellen benötigen Vacuum.\n');
await sequelize.close();
process.exit(0);
}
console.log(`📋 Gefunden: ${tablesToVacuum.length} Tabellen benötigen Vacuum\n`);
// Zeige Tabellen
console.log('Tabellen, die gevacuumt werden:');
tablesToVacuum.forEach((t, i) => {
console.log(` ${i + 1}. ${t.table_name}`);
console.log(` Größe: ${t.table_size}, Tote Zeilen: ${parseInt(t.n_dead_tup).toLocaleString()} (${t.dead_percent}%)`);
});
console.log('');
// Frage Bestätigung
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const answer = await new Promise((resolve) => {
rl.question('Möchtest du VACUUM ANALYZE auf diesen Tabellen ausführen? (j/n): ', resolve);
});
rl.close();
if (answer.toLowerCase() !== 'j' && answer.toLowerCase() !== 'y' && answer.toLowerCase() !== 'ja' && answer.toLowerCase() !== 'yes') {
console.log('❌ Abgebrochen.\n');
await sequelize.close();
process.exit(0);
}
console.log('\n🧹 Starte Vacuum...\n');
// Führe VACUUM ANALYZE aus
for (let i = 0; i < tablesToVacuum.length; i++) {
const table = tablesToVacuum[i];
const startTime = Date.now();
try {
console.log(`[${i + 1}/${tablesToVacuum.length}] Vacuuming ${table.table_name}...`);
await sequelize.query(`VACUUM ANALYZE ${table.table_name};`);
const duration = ((Date.now() - startTime) / 1000).toFixed(2);
console.log(` ✅ Fertig in ${duration}s\n`);
} catch (error) {
console.error(` ❌ Fehler: ${error.message}\n`);
}
}
console.log('='.repeat(60));
console.log('✅ Vacuum abgeschlossen\n');
await sequelize.close();
process.exit(0);
} catch (error) {
console.error('❌ Fehler:', error.message);
console.error(error.stack);
process.exit(1);
}
}
main();