From c5c5937978f0c24d3b4ea6842c24bf80f6aa8148 Mon Sep 17 00:00:00 2001 From: Wendell Jones Date: Tue, 7 Jul 2026 08:58:53 -0400 Subject: [PATCH] Update .gitignore and remove old weekly report files; enhance performance monitor script with VACUUM ANALYZE option --- .gitignore | 2 +- .../weekly/diagnostics_2026-03-05_080020.txt | Bin 1804 -> 0 bytes .../weekly/index_usage_2026-03-05_080020.txt | 0 .../query_analysis_2026-03-05_080020.txt | Bin 293924 -> 0 bytes reports/weekly/summary_2026-03-05_080020.txt | 50 --------- .../weekly/table_sizes_2026-03-05_080020.txt | 0 scripts/Weekly-Performance-Monitor.ps1 | 101 ++++++++++++++++-- scripts/db-config.ps1 | 6 +- 8 files changed, 98 insertions(+), 61 deletions(-) delete mode 100644 reports/weekly/diagnostics_2026-03-05_080020.txt delete mode 100644 reports/weekly/index_usage_2026-03-05_080020.txt delete mode 100644 reports/weekly/query_analysis_2026-03-05_080020.txt delete mode 100644 reports/weekly/summary_2026-03-05_080020.txt delete mode 100644 reports/weekly/table_sizes_2026-03-05_080020.txt 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 ec98008ebbfdeae23892d31bc4435e1ac6cf2936..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1804 zcmb`H-AW@t5QXbn*ms!A$f6is6c-d!f<$CBCf@AL5KWXoGLA`HH{i3Y->IHSCOQcs zq3NEkQ$Jl@eWu=CTh_AyGqHP%_>Nc~kT&gyty#_fTWW3Oj&+$4Q#PsfK_^p>C$OMz z+mnrnFhD=X_Lgs-G07qx*s6!}Jmd6m%AMaOvP*Ipk-@zU(KW4M=VW`yerR{-hMr+U zeqHb@*3{9l-+R?L9(Yt`TBQz=o!Eu_Vbs{QtnQMsyI?5KCg>6Knw`8hRz4Ip@|PO> z3&m7I@19VJTYou8V#5owonimHT0(j-q5nyWz4|f!Lww|)dn#HU>n5voV2q+YW7qRj z0}sD`=bxV{Gmp$m%j;NXTZV<&MdZC~Y-RVI*~}yJ(#)!CS7uv|DYptNi+F(t>bgE< zUij0y_8Z+kEZk+?V?W@P^yfqo>JK>7$~0BnWxw`kZYiI)+Tbr4*e|s{eeH7 z?8N1j{*E?OJ$2>Ucj{qC6FS3}!`Uw@|zYw?09Xq7Vpn~Qm`X0JQE E13&)X0{{R3 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 ee3c901418d909424b48a9e6751b2ca4408c8344..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 293924 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 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