114 lines
4.0 KiB
JavaScript
Executable File
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();
|