diff --git a/.gitignore b/.gitignore index 3d4c1bbc..791fcbc2 100644 --- a/.gitignore +++ b/.gitignore @@ -42,7 +42,7 @@ Properties/PublishProfiles/ # Ignore schema directories (database schema dumps) **/schema/ schema/ - +[Rr]eports/ # Ignore .idea IDE directory **/.idea/ .idea/ diff --git a/reports/weekly/diagnostics_2026-03-05_080020.txt b/reports/weekly/diagnostics_2026-03-05_080020.txt deleted file mode 100644 index ec98008e..00000000 Binary files a/reports/weekly/diagnostics_2026-03-05_080020.txt and /dev/null differ diff --git a/reports/weekly/index_usage_2026-03-05_080020.txt b/reports/weekly/index_usage_2026-03-05_080020.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/reports/weekly/query_analysis_2026-03-05_080020.txt b/reports/weekly/query_analysis_2026-03-05_080020.txt deleted file mode 100644 index ee3c9014..00000000 Binary files a/reports/weekly/query_analysis_2026-03-05_080020.txt and /dev/null differ diff --git a/reports/weekly/summary_2026-03-05_080020.txt b/reports/weekly/summary_2026-03-05_080020.txt deleted file mode 100644 index a5276746..00000000 --- a/reports/weekly/summary_2026-03-05_080020.txt +++ /dev/null @@ -1,50 +0,0 @@ -================================================================================ -JELLYFIN DATABASE WEEKLY PERFORMANCE SUMMARY -================================================================================ -Report Date: 2026-03-05 08:00:21 -Database: jellyfin_testdata at 192.168.129.248 -Week Number: 10 -================================================================================ - -REPORTS GENERATED: - * diagnostics_2026-03-05_080020.txt : Full diagnostic report - * query_analysis_2026-03-05_080020.txt : Slow query analysis - * index_usage_2026-03-05_080020.txt : Index usage statistics - * table_sizes_2026-03-05_080020.txt : Table size and bloat info - * summary_2026-03-05_080020.txt : This summary - -================================================================================ -QUICK HEALTH CHECK: -================================================================================ - -To view critical issues check diagnostics file for: - * Section 5: TABLES WITH HIGH SEQUENTIAL SCANS - * Section 8: UNUSED OR RARELY USED INDEXES - * Section 10: SLOWEST QUERIES - -To identify slow queries check query_analysis file for: - * Section 2: TOP 20 SLOWEST QUERIES (by max execution time) - * Section 5: BASEITEMS TABLE QUERIES ANALYSIS - -================================================================================ -RECOMMENDED ACTIONS: -================================================================================ - - * Review slow queries taking longer than 10 seconds - * Check if new indexes are being utilized - * Look for tables with many sequential scans - * Monitor cache hit ratio (should be above 95 percent) - * Check for tables with high dead tuples percentage (need VACUUM) - -================================================================================ -AUTOMATION SETUP: -================================================================================ - -To schedule this script weekly via Task Scheduler run Setup-Weekly-Monitor.ps1 -as Administrator. This will create a scheduled task to run every Monday at 6am. - -To disable the scheduled task run Setup-Weekly-Monitor.ps1 with -Uninstall flag. - -================================================================================ -NEXT REPORT: 03/05/2026 08:00:21.AddDays(7).ToString("yyyy-MM-dd") -================================================================================ diff --git a/reports/weekly/table_sizes_2026-03-05_080020.txt b/reports/weekly/table_sizes_2026-03-05_080020.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/scripts/Weekly-Performance-Monitor.ps1 b/scripts/Weekly-Performance-Monitor.ps1 index f0208e5e..f8255038 100644 --- a/scripts/Weekly-Performance-Monitor.ps1 +++ b/scripts/Weekly-Performance-Monitor.ps1 @@ -10,7 +10,8 @@ 2. Analyzes slow queries 3. Tracks index usage over time 4. Generates weekly performance reports - 5. Can be scheduled via Task Scheduler + 5. Optionally runs VACUUM ANALYZE on tables with high dead tuples + 6. Can be scheduled via Task Scheduler .PARAMETER OutputDirectory Directory to save reports (default: reports/weekly) @@ -27,6 +28,9 @@ .EXAMPLE .\Weekly-Performance-Monitor.ps1 -OutputDirectory "C:\Reports\Jellyfin" -CompareWithPrevious +.EXAMPLE + .\Weekly-Performance-Monitor.ps1 -AutoVacuumAnalyze -DeadTupleThreshold 10000 -DeadTuplePercentThreshold 20 + .EXAMPLE # Schedule weekly via Task Scheduler $action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-File C:\Path\To\Weekly-Performance-Monitor.ps1" @@ -47,7 +51,18 @@ param ( [switch]$EmailReport, [Parameter()] - [switch]$CompareWithPrevious + [switch]$CompareWithPrevious, + + [Parameter()] + [switch]$AutoVacuumAnalyze, + + [Parameter()] + [ValidateRange(1, [int]::MaxValue)] + [int]$DeadTupleThreshold = 100, + + [Parameter()] + [ValidateRange(0.01, 100.0)] + [double]$DeadTuplePercentThreshold = 0.01 ) # ============================================================================ @@ -189,16 +204,16 @@ function Get-TableSizeStats { $query = @" SELECT schemaname, - tablename, - pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS total_size, - pg_size_pretty(pg_relation_size(schemaname||'.'||tablename)) AS table_size, - pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename) - pg_relation_size(schemaname||'.'||tablename)) AS index_size, + relname AS tablename, + pg_size_pretty(pg_total_relation_size(schemaname||'.'||relname)) AS total_size, + pg_size_pretty(pg_relation_size(schemaname||'.'||relname)) AS table_size, + pg_size_pretty(pg_total_relation_size(schemaname||'.'||relname) - pg_relation_size(schemaname||'.'||relname)) AS index_size, n_live_tup AS row_count, n_dead_tup AS dead_rows, ROUND(100.0 * n_dead_tup / NULLIF(n_live_tup, 0), 2) AS dead_pct FROM pg_stat_user_tables WHERE schemaname IN ('library', 'users', 'authentication', 'displaypreferences', 'activitylog') -ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC; +ORDER BY pg_total_relation_size(schemaname||'.'||relname) DESC; "@ try { @@ -217,6 +232,67 @@ ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC; } } +function Invoke-ConditionalVacuumAnalyze { + param( + [int]$MinDeadTuples, + [double]$MinDeadPercent + ) + + Write-ColorOutput "`nChecking for tables that need VACUUM ANALYZE..." -Color Cyan + Write-ColorOutput "Thresholds: dead tuples >= $MinDeadTuples and dead percent >= $MinDeadPercent" -Color Gray + + $tableQuery = @" +SELECT + format('%I.%I', schemaname, relname) AS qualified_table, + n_dead_tup, + ROUND(100.0 * n_dead_tup / NULLIF(n_live_tup, 0), 2) AS dead_pct +FROM pg_stat_user_tables +WHERE schemaname IN ('library', 'users', 'authentication', 'displaypreferences', 'activitylog') + AND n_live_tup > 0 + AND n_dead_tup >= $MinDeadTuples + AND (100.0 * n_dead_tup / NULLIF(n_live_tup, 0)) >= $MinDeadPercent +ORDER BY n_dead_tup DESC; +"@ + + try { + $tables = & $PSQL_PATH -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -t -A -F "|" -c $tableQuery 2>&1 + if ($LASTEXITCODE -ne 0) { + Write-ColorOutput "[ERROR] Failed to evaluate dead tuple thresholds: $tables" -Color Red + return $false + } + + $tableLines = @($tables | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) + if ($tableLines.Count -eq 0) { + Write-ColorOutput "[OK] No tables exceeded dead tuple thresholds" -Color Green + return $true + } + + Write-ColorOutput "Found $($tableLines.Count) table(s) requiring VACUUM ANALYZE" -Color Yellow + + foreach ($line in $tableLines) { + $parts = $line -split "\|", 3 + $qualifiedTable = $parts[0].Trim() + $deadTuples = if ($parts.Count -ge 2) { $parts[1].Trim() } else { "unknown" } + $deadPercent = if ($parts.Count -ge 3) { $parts[2].Trim() } else { "unknown" } + + Write-ColorOutput "Running VACUUM ANALYZE on $qualifiedTable (dead tuples: $deadTuples, dead percent: $deadPercent)" -Color Cyan + + $vacuumResult = & $PSQL_PATH -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -c "VACUUM (ANALYZE, VERBOSE) $qualifiedTable;" 2>&1 + if ($LASTEXITCODE -ne 0) { + Write-ColorOutput "[ERROR] VACUUM ANALYZE failed for ${qualifiedTable}: $vacuumResult" -Color Red + return $false + } + + Write-ColorOutput "[OK] VACUUM ANALYZE completed for $qualifiedTable" -Color Green + } + + return $true + } catch { + Write-ColorOutput "[ERROR] Conditional VACUUM ANALYZE failed: $_" -Color Red + return $false + } +} + function Compare-WithPrevious { param( [string]$CurrentReport, @@ -361,6 +437,17 @@ Get-IndexUsageStats -OutputFile $indexUsageFile | Out-Null $tableSizesFile = Join-Path $reportDir "table_sizes_$timestamp.txt" Get-TableSizeStats -OutputFile $tableSizesFile | Out-Null +# Optionally vacuum/analyze tables that exceed dead tuple thresholds +if ($AutoVacuumAnalyze) { + $vacuumSuccess = Invoke-ConditionalVacuumAnalyze ` + -MinDeadTuples $DeadTupleThreshold ` + -MinDeadPercent $DeadTuplePercentThreshold + + if (-not $vacuumSuccess) { + Write-ColorOutput "[WARN] Conditional VACUUM ANALYZE encountered errors" -Color Yellow + } +} + # Compare with previous week if requested if ($CompareWithPrevious) { Compare-WithPrevious -CurrentReport $diagnosticsFile -OutputDirectory $reportDir diff --git a/scripts/db-config.ps1 b/scripts/db-config.ps1 index 8d17c56a..f3c036d4 100644 --- a/scripts/db-config.ps1 +++ b/scripts/db-config.ps1 @@ -4,9 +4,9 @@ # Database connection settings $PSQL_PATH = "C:\Program Files\PostgreSQL\18\bin\psql.exe" $DB_USER = "jellyfin" -$DB_NAME = "jellyfin" # ← Change this to switch databases -$DB_HOST = "192.168.129.163" -$DB_PORT = "5432" +$DB_NAME = "jellyfin_test2" # ← Change this to switch databases +$DB_HOST = "192.168.129.253" +$DB_PORT = "6432" # Export for use in other scripts $global:PSQL_PATH = $PSQL_PATH