From 81f8a702dec1288a842154488e6f3a39aad4be59 Mon Sep 17 00:00:00 2001 From: Wendell Jones Date: Tue, 7 Jul 2026 08:31:33 -0400 Subject: [PATCH] feat: add conditional VACUUM ANALYZE support to weekly monitoring scripts --- docs/DEAD_TUPLE_AUTOVACUUM_GUIDE.md | 67 ++++++++++ docs/MASTER_DOCS_INDEX.md | 174 +++++++++++++++++++++++++ scripts/Setup-Weekly-Monitor.ps1 | 40 +++++- scripts/Weekly-Performance-Monitor.ps1 | 91 ++++++++++++- 4 files changed, 367 insertions(+), 5 deletions(-) create mode 100644 docs/DEAD_TUPLE_AUTOVACUUM_GUIDE.md create mode 100644 docs/MASTER_DOCS_INDEX.md diff --git a/docs/DEAD_TUPLE_AUTOVACUUM_GUIDE.md b/docs/DEAD_TUPLE_AUTOVACUUM_GUIDE.md new file mode 100644 index 00000000..67761370 --- /dev/null +++ b/docs/DEAD_TUPLE_AUTOVACUUM_GUIDE.md @@ -0,0 +1,67 @@ +# Dead Tuple Auto-Vacuum Guide + +## What was added + +The weekly PostgreSQL monitoring workflow now supports **conditional `VACUUM ANALYZE`** based on dead tuple thresholds. + +This was added to: + +- `scripts\Weekly-Performance-Monitor.ps1` +- `scripts\Setup-Weekly-Monitor.ps1` + +When enabled, the monitor checks `pg_stat_user_tables` and runs: + +```sql +VACUUM (ANALYZE, VERBOSE) schema.table_name; +``` + +only for tables that exceed both configured thresholds. + +## New monitor parameters + +`scripts\Weekly-Performance-Monitor.ps1` now supports: + +- `-AutoVacuumAnalyze` + Enables conditional vacuum/analyze. +- `-DeadTupleThreshold ` + Minimum dead tuples required (default: `10000`). +- `-DeadTuplePercentThreshold ` + Minimum dead tuple percentage required (default: `20.0`). + +## How to run manually + +```powershell +.\scripts\Weekly-Performance-Monitor.ps1 -AutoVacuumAnalyze +``` + +Example with custom thresholds: + +```powershell +.\scripts\Weekly-Performance-Monitor.ps1 -AutoVacuumAnalyze -DeadTupleThreshold 15000 -DeadTuplePercentThreshold 25 +``` + +## How to enable in the scheduled task + +`scripts\Setup-Weekly-Monitor.ps1` now supports: + +- `-EnableAutoVacuumAnalyze` +- `-DeadTupleThreshold ` +- `-DeadTuplePercentThreshold ` + +Create or update the weekly task with auto-vacuum enabled: + +```powershell +.\scripts\Setup-Weekly-Monitor.ps1 -EnableAutoVacuumAnalyze -DeadTupleThreshold 10000 -DeadTuplePercentThreshold 20 +``` + +The setup script passes these options to the weekly monitor when the scheduled task runs. + +## Notes + +- Conditional vacuum is **opt-in** and disabled unless you pass the enable flag. +- Threshold checks are currently scoped to these schemas: + - `library` + - `users` + - `authentication` + - `displaypreferences` + - `activitylog` diff --git a/docs/MASTER_DOCS_INDEX.md b/docs/MASTER_DOCS_INDEX.md new file mode 100644 index 00000000..94ed4820 --- /dev/null +++ b/docs/MASTER_DOCS_INDEX.md @@ -0,0 +1,174 @@ +# Master Documentation Index + +This file is a generated master index of markdown documentation under `docs/`, with a consolidated summary of major change themes represented in the documentation corpus. + +## Change Summary (Derived from Documentation Content) + +Total markdown files indexed: **151** + +| Theme | Files mentioning theme | Summary | +|---|---:|---| +| PostgreSQL and database migration | 130 | Migration work, provider updates, backup/restore, and PostgreSQL-first deployment guidance. | +| Performance and query optimization | 111 | Index strategy, diagnostics, slow-query remediation, and maintenance automation. | +| Startup and configuration | 136 | Startup.json behavior, OS/path handling, and runtime configuration fixes. | +| Build and installer workflow | 93 | Build stabilization, installer updates, and SQL file publish packaging fixes. | +| Async and runtime behavior | 80 | Async conversion progress, runtime safety, and auth/websocket handling updates. | +| Schema and data model changes | 103 | Schema conversion notes, entity/table alignment, and constraint compatibility changes. | + +### Overall Change Narrative + +The documented work shows a strong PostgreSQL-focused modernization effort across the project, including migration from SQLite paths, schema alignment, and operational hardening. Performance tuning is a major thread (indexes, diagnostics, query fixes), with complementary updates to startup/config behavior and packaging workflows. The docs also capture broad async conversion progress and stability fixes in runtime and data access paths. Recent additions include conditional dead-tuple-based `VACUUM ANALYZE` automation in the weekly monitoring workflow. + +## Table of Contents + +- [🚀 Add Supplementary Indexes to jellyfin_testsdata](ADD_INDEXES_GUIDE.md) +- [Repository Async Conversion Checklist](ASYNC_CONVERSION_CHECKLIST.md) +- [Repository Async Conversion Priority List](ASYNC_CONVERSION_PRIORITY.md) +- [Jellyfin Async Migration Plan](ASYNC_MIGRATION_PLAN.md) +- [✅ Auto-Apply Performance Indexes - Implementation Complete!](AUTO_APPLY_INDEXES_COMPLETE.md) +- [Auto-Generated Startup Configuration](AUTO_GENERATED_STARTUP_CONFIG.md) +- [Summary: Auto-Generated startup.json Configuration](AUTO_GENERATION_SUMMARY.md) +- [Automatic Database Creation - Implementation Summary](AUTOMATIC_DATABASE_CREATION_SUMMARY.md) +- [Automatic Database Creation - PostgreSQL](AUTOMATIC_DATABASE_CREATION.md) +- [BASEITEM_ASYNC_REFERENCE](BASEITEM_ASYNC_REFERENCE.md) +- [BaseItemRepository Async Conversion - Final Status Report](BASEITEM_FINAL_STATUS.md) +- [Build Installer Script - Fixed!](BUILD_INSTALLER_FIXED.md) +- [Build Success Summary - Emby.Naming Project](BUILD_SUCCESS_SUMMARY.md) +- [Build Error Resolution Guide](build-error-resolution.md) +- [Centralized lib Folder Build Configuration](CENTRALIZED_LIB_FOLDER.md) +- [Code Proof: startup.json IS Checked on Startup](CODE_PROOF_STARTUP_JSON.md) +- [Complete Fix Summary: SQLite Removal + EF Core Optimization](COMPLETE_FIX_SUMMARY.md) +- [Complete Session Summary - Jellyfin PostgreSQL Error Fixes](COMPLETE-SESSION-SUMMARY.md) +- [Jellyfin Contributors](CONTRIBUTORS.md) +- [Correlated Subquery Performance Fix](CORRELATED_SUBQUERY_FIX.md) +- [CS0006 Error Fix - SQLite References Removal](CS0006_SQLITE_FIX.md) +- [📊 Database Performance Analysis - Your Results](DATABASE_ANALYSIS_REPORT.md) +- [Database Configuration Guide](DATABASE_CONFIGURATION_GUIDE.md) +- [Database Configuration Guide](DATABASE_CONFIGURATION.md) +- [Database Schema Mismatch - Detailed Entity Mapping](DATABASE_MISMATCH_DETAILED_ENTITY_MAP.md) +- [Visual Schema Mismatch Comparison](DATABASE_MISMATCH_VISUAL_COMPARISON.md) +- [Summary: Multi-Database Configuration with Presets](DATABASE_PRESETS_FEATURE.md) +- [Database Schema Creation - How It Works](DATABASE_SCHEMA_CREATION.md) +- [PostgreSQL Database Schema Mismatch Report](DATABASE_SCHEMA_MISMATCH_REPORT.md) +- [PostgreSQL Database Views](DATABASE_VIEWS.md) +- [PostgreSQL Database Configuration Examples](database-configuration-examples.md) +- [Database Constraint Violation - BaseItemProviders Duplicate Key](database-constraint-violation-baseitem-providers.md) +- [Database Deadlock Handling - Item Deletion](database-deadlock-handling.md) +- [Database Query Optimization Guide](database-query-optimization.md) +- [How to run EFCore migrations](database/jellyfin-database-readme.md) +- [PostgreSQL Backup Configuration](database/postgres-backup-configuration.md) +- [PostgreSQL Database Provider - Migration Guide](database/postgres-migration-guide.md) +- [Jellyfin.Database.Providers.Postgres](database/postgres-provider-readme.md) +- [Dead Tuple Auto-Vacuum Guide](DEAD_TUPLE_AUTOVACUUM_GUIDE.md) +- [✅ Remote Database Diagnostics Complete!](DIAGNOSTICS_COMPLETE_SUMMARY.md) +- [Documentation Organization Complete!](DOCUMENTATION_ORGANIZATION.md) +- [✅ Documentation Reorganization Complete!](DOCUMENTATION_REORGANIZATION.md) +- [EditorConfig Setup for Emby.Naming Project](EDITORCONFIG_SETUP.md) +- [EF Core QuerySplittingBehavior Warning Fix](EF_CORE_QUERY_SPLITTING_FIX.md) +- [EF Core Query Translation Fix](EF_CORE_QUERY_TRANSLATION_FIX.md) +- [EF Core Removal Analysis — pgsql-jellyfin Fork](EF_CORE_REMOVAL_ANALYSIS.md) +- [EF Core Change Tracking Conflict Fix - BaseItemProvider UPSERT](ef-core-tracking-conflict-fix.md) +- [Exception Middleware - Authentication Error Messaging Improvement](exception-middleware-authentication-messaging.md) +- [Database Table Verification and Auto-Creation Feature](FEATURE_DATABASE_TABLE_VERIFICATION.md) +- [Summary: File-Based Startup Configuration](FILE_BASED_CONFIG_SUMMARY.md) +- [File-Based Startup Configuration](FILE_BASED_STARTUP_CONFIG.md) +- [Fix: Empty database.xml on Startup](FIX_EMPTY_DATABASE_XML.md) +- [Check and Fix system.xml MetadataPath on Windows](FIX_SYSTEM_XML_PATHS.md) +- [Generate PostgreSQL Migrations from SQLite](GENERATE_POSTGRESQL_MIGRATIONS.md) +- [Git Commit: OS-Specific startup.json Generation](GIT_COMMIT_OS_STARTUP.md) +- [PublishProfiles Added to .gitignore](GITIGNORE_PUBLISHPROFILES.md) +- [How to Ignore IDE0065 Error Message](HOW_TO_IGNORE_IDE0065.md) +- [🎯 How to Switch Database Connections](HOW_TO_SWITCH_DATABASE.md) +- [✅ Supplementary Index Checker - Implementation Complete!](IMPLEMENTATION_COMPLETE.md) +- [Increasing Database Command Timeout](increase-database-timeout.md) +- [Creating an Installer for Jellyfin](INSTALLER_GUIDE.md) +- [🔍 Jellyfin Setup Installer Analysis](INSTALLER_NEEDS_UPDATE.md) +- [Quick Installer Setup](INSTALLER_QUICK_START.md) +- [✅ Jellyfin Installer Updated!](INSTALLER_UPDATE_COMPLETE.md) +- [✅ ItemValues Indexes Added to performance_indexes.sql](ITEMVALUES_INDEXES_ADDED.md) +- [📊 Fresh Diagnostics Analysis - Remote Database (2026-02-28)](LATEST_DIAGNOSTICS_ANALYSIS.md) +- [Quick Reference: Centralized lib Folder](LIB_FOLDER_QUICK_REF.md) +- [Library Options DB Design](LIBRARY_OPTIONS_DB_DESIGN.md) +- [Library Monitor Delay Configuration](library-monitor-delay-configuration.md) +- [LibraryMonitor Disposal Fix - Summary](LIBRARYMONITOR_DISPOSAL_FIX_SUMMARY.md) +- [ObjectDisposedException in LibraryMonitor Fix](LIBRARYMONITOR_DISPOSAL_FIX.md) +- [Summary: Linux Paths Showing on Windows](LINUX_PATH_ISSUE_SUMMARY.md) +- [Fix Jellyfin Marker Conflict Error](MARKER_CONFLICT_FIX.md) +- [🎉 MediaAttachmentRepository Async Conversion - Complete!](MEDIAATTACHMENT_CONVERSION_COMPLETE.md) +- [Merge Migrations - Instructions](MERGE_MIGRATIONS_GUIDE.md) +- [PostgreSQL Migration Recovery Fix](MIGRATION_RECOVERY_FIX.md) +- [✅ PostgreSQL Migration Successfully Applied!](MIGRATION_SUCCESS_REPORT.md) +- [PostgreSQL Migration Verification Report](MIGRATION_VERIFICATION_REPORT.md) +- [🔧 Fixed: Missing Base Performance Indexes](MISSING_INDEXES_FIXED.md) +- [.NET 11 Migration Summary](NET11_MIGRATION_SUMMARY.md) +- [PostgreSQL "Command Already in Progress" - Fix](NPGSQL_COMMAND_IN_PROGRESS_FIX.md) +- [Option 1 Implementation Complete! ✅](OPTION1_IMPLEMENTATION_COMPLETE.md) +- [OS-Specific startup.json Generation](OS_SPECIFIC_STARTUP_CONFIG.md) +- [🎊 ASYNC MIGRATION PROJECT - OVERALL STATUS](OVERALL_PROJECT_STATUS.md) +- [Jellyfin Path Configuration Guide](PATH_CONFIGURATION_GUIDE.md) +- [Windows Path Fix for Jellyfin PostgreSQL Database](PATH_FIX_GUIDE.md) +- [Database Performance Monitoring - Quick Start Guide](PERFORMANCE_MONITORING_GUIDE.md) +- [Performance Monitoring Setup Complete - Summary](PERFORMANCE_SETUP_COMPLETE.md) +- [Phase 3 BaseItemRepository Async Conversion - Combined Summary](PHASE_3_COMBINED_SUMMARY.md) +- [Phase 3A: Query Operations Async Conversion - Final Summary](PHASE_3A_FINAL_SUMMARY.md) +- [Phase 3b: Item Retrieval Async Conversion - Summary](PHASE_3B_SUMMARY.md) +- [Phase 3C & 3D: Write & Delete Operations Async Conversion - Summary](PHASE_3C_3D_SUMMARY.md) +- [Phase 3E: Aggregations and Statistics Async Conversion - Summary](PHASE_3E_SUMMARY.md) +- [🎉 Phase 1 COMPLETE! All Repositories Async Converted](PHASE1_COMPLETE.md) +- [🎉 Phase 1 Complete! Repository Async Conversion Status](PHASE1_PROGRESS_REPORT.md) +- [🎉 Phase 2 COMPLETE! PeopleRepository Async Converted](PHASE2_COMPLETE.md) +- [🔥 Phase 3 Strategy: BaseItemRepository Async Conversion](PHASE3_STRATEGY.md) +- [🎉 Sub-Phase 3a COMPLETE! BaseItemRepository Query Operations Converted](PHASE3A_COMPLETE.md) +- [🎉 Async Conversion POC - Summary Report](POC_SUMMARY_REPORT.md) +- [PostgreSQL Backup Configuration - Implementation Summary](POSTGRES_BACKUP_IMPLEMENTATION.md) +- [PostgreSQL Backup - Local-Only Implementation Summary](POSTGRES_BACKUP_LOCAL_ONLY_IMPLEMENTATION.md) +- [PostgreSQL Backup Logging - External Tool References](POSTGRES_BACKUP_LOGGING_EXTERNAL_TOOLS.md) +- [PostgreSQL Backup and Restore Analysis](POSTGRESQL_BACKUP_ANALYSIS.md) +- [PostgreSQL Migration Generation - Complete! ✅](POSTGRESQL_MIGRATION_COMPLETE.md) +- [PostgreSQL Migration Generation - Summary](POSTGRESQL_MIGRATION_GENERATED.md) +- [PostgreSQL Troubleshooting Guide](POSTGRESQL_TROUBLESHOOTING.md) +- [PostgreSQL UUID Aggregate Fix](postgresql-uuid-aggregate-fix.md) +- [✅ PowerShell Script Fixed!](POWERSHELL_SCRIPT_FIXED.md) +- [🎉 JELLYFIN ASYNC CONVERSION - PROJECT COMPLETE! 🎉](PROJECT_COMPLETION.md) +- [Query Grouping Performance - Current Status and Workarounds](query-grouping-current-status.md) +- [Query Grouping Optimization - Final Solution](query-grouping-final-solution.md) +- [Query Optimization Journey - Complete Story](query-optimization-complete-story.md) +- [⚡ Quick Action Plan - Based on Your Diagnostics](QUICK_ACTION_PLAN.md) +- [🚀 Quick Reference: Publish Jellyfin with SQL Files](QUICK_PUBLISH_REFERENCE.md) +- [Quick Reference: Auto-Migration & Index Checker](QUICK_REFERENCE.md) +- [Quick Start: PostgreSQL with Jellyfin](QUICKSTART_POSTGRESQL.md) +- [Summary: EditorConfig Configuration for Emby.Naming Project](README_EDITORCONFIG_CHANGES.md) +- [Comprehensive README.md Generated!](README_GENERATION.md) +- [README.md Update Summary](README_UPDATE_SUMMARY.md) +- [Jellyfin with PostgreSQL Support](README-POSTGRESQL.md) +- [Jellyfin with PostgreSQL Support](README.md) +- [README.original](README.original.md) +- [✅ Remote Database Analysis - Quick Summary](REMOTE_ANALYSIS_SUMMARY.md) +- [📊 Remote Database Analysis - Updated](REMOTE_DATABASE_ANALYSIS.md) +- [Remote PostgreSQL Backup Support](remote-postgresql-backup-support.md) +- [SQL Column Conversion Reference - All Tables](SCHEMA_COLUMN_CONVERSION_REFERENCE.md) +- [SQL Schema Conversion Guide - PascalCase to snake_case](SCHEMA_CONVERSION_GUIDE.md) +- [Quick Connect and Add Supplementary Indexes](scripts/CONNECT_AND_UPDATE.md) +- [Complete Session Summary - All Tasks Accomplished](SESSION_SUMMARY.md) +- [✅ SQL Files Not Included in Publish - Solution](SQL_FILES_PUBLISH_FIX.md) +- [✅ SQL Files Publish Issue - FIXED!](SQL_PUBLISH_ISSUE_RESOLVED.md) +- [SQLite-Specific Migration Filtering - Fix Summary](SQLITE_MIGRATION_FILTERING_FIX.md) +- [Removing SQLite from PostgreSQL-Only Deployment](SQLITE_REMOVAL_PLAN.md) +- [SQLite Migration Filtering Fix](sqlite-migration-filtering-fix.md) +- [🎯 Jellyfin Database Async Migration](STAKEHOLDER_PRESENTATION.md) +- [🎯 READY TO RUN - Add Supplementary Indexes](START_HERE.md) +- [Comparison: Before vs After](STARTUP_CONFIG_COMPARISON.md) +- [Startup Configuration Update Summary](STARTUP_CONFIG_UPDATE.md) +- [Fixing startup.json - OS-Specific Defaults Not Applied](STARTUP_JSON_FIX.md) +- [Startup.json Configuration Fix - Marker Conflict Resolution](STARTUP_JSON_MARKER_FIX.md) +- [How startup.json is Loaded and Used on Startup](STARTUP_JSON_VERIFICATION.md) +- [Visual Guide: startup.json Auto-Generation](STARTUP_JSON_VISUAL_GUIDE.md) +- [Fix: startup.json Still Has Wrong WebDir](STARTUP_JSON_WEBDIR_FIX.md) +- [SyncPlay Authorization Error Handling Improvement](syncplay-authorization-error-handling.md) +- [Summary: Path Configuration Enhancement](TEMP_DIR_CONFIGURATION_FEATURE.md) +- [Troubleshooting: EF Core Pending Model Changes Warning](TROUBLESHOOTING_EF_PENDING_CHANGES.md) +- [Version Update to 11.0.0-PostgreSQL PREVIEW](VERSION_UPDATE_11.0.0_PREVIEW.md) +- [✅ WebDir Path Issue - FULLY FIXED](WEBDIR_ISSUE_COMPLETE_FIX.md) +- [WebSocket Authentication Guide](WEBSOCKET_AUTHENTICATION.md) +- [WebSocket "Token is required" Error - Not a Bug!](WEBSOCKET_TOKEN_REQUIRED_ERROR.md) +- [📊 Weekly Index Usage Tracking](WEEKLY_TRACKING.md) diff --git a/scripts/Setup-Weekly-Monitor.ps1 b/scripts/Setup-Weekly-Monitor.ps1 index 130e0c60..bf288669 100644 --- a/scripts/Setup-Weekly-Monitor.ps1 +++ b/scripts/Setup-Weekly-Monitor.ps1 @@ -6,7 +6,8 @@ Sets up automated weekly performance monitoring via Windows Task Scheduler .DESCRIPTION - Creates a scheduled task to run Weekly-Performance-Monitor.ps1 every Monday at 6 AM + Creates a scheduled task to run Weekly-Performance-Monitor.ps1 every Monday at 6 AM. + Can optionally enable conditional VACUUM ANALYZE based on dead tuple thresholds. .PARAMETER Uninstall Remove the scheduled task @@ -17,12 +18,24 @@ .PARAMETER DayOfWeek Day of week to run (default: Monday) +.PARAMETER EnableAutoVacuumAnalyze + Enable conditional VACUUM ANALYZE for tables above dead tuple thresholds + +.PARAMETER DeadTupleThreshold + Minimum dead tuples before VACUUM ANALYZE runs (default: 10000) + +.PARAMETER DeadTuplePercentThreshold + Minimum dead tuple percentage before VACUUM ANALYZE runs (default: 20) + .EXAMPLE .\Setup-Weekly-Monitor.ps1 .EXAMPLE .\Setup-Weekly-Monitor.ps1 -TaskTime "3am" -DayOfWeek Sunday +.EXAMPLE + .\Setup-Weekly-Monitor.ps1 -EnableAutoVacuumAnalyze -DeadTupleThreshold 10000 -DeadTuplePercentThreshold 20 + .EXAMPLE .\Setup-Weekly-Monitor.ps1 -Uninstall #> @@ -37,7 +50,18 @@ param ( [Parameter()] [ValidateSet('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday')] - [string]$DayOfWeek = 'Monday' + [string]$DayOfWeek = 'Monday', + + [Parameter()] + [switch]$EnableAutoVacuumAnalyze, + + [Parameter()] + [ValidateRange(1, [int]::MaxValue)] + [int]$DeadTupleThreshold = 10000, + + [Parameter()] + [ValidateRange(0.01, 100.0)] + [double]$DeadTuplePercentThreshold = 20.0 ) $ErrorActionPreference = "Stop" @@ -104,6 +128,11 @@ Write-ColorOutput "Configuration:" -Color Cyan Write-ColorOutput " Task Name: $TaskName" -Color White Write-ColorOutput " Script: $MonitorScript" -Color White Write-ColorOutput " Schedule: Every $DayOfWeek at $TaskTime" -Color White +if ($EnableAutoVacuumAnalyze) { + Write-ColorOutput " Auto VACUUM ANALYZE: Enabled (dead tuples >= $DeadTupleThreshold, dead percent >= $DeadTuplePercentThreshold)" -Color White +} else { + Write-ColorOutput " Auto VACUUM ANALYZE: Disabled" -Color White +} Write-ColorOutput "" # Remove existing task if present @@ -116,9 +145,14 @@ if ($existingTask) { # Create scheduled task action Write-ColorOutput "Creating scheduled task..." -Color Cyan +$scriptArguments = "-NoProfile -ExecutionPolicy Bypass -File `"$MonitorScript`"" +if ($EnableAutoVacuumAnalyze) { + $scriptArguments += " -AutoVacuumAnalyze -DeadTupleThreshold $DeadTupleThreshold -DeadTuplePercentThreshold $DeadTuplePercentThreshold" +} + $action = New-ScheduledTaskAction ` -Execute "PowerShell.exe" ` - -Argument "-NoProfile -ExecutionPolicy Bypass -File `"$MonitorScript`"" + -Argument $scriptArguments # Create trigger $trigger = New-ScheduledTaskTrigger ` diff --git a/scripts/Weekly-Performance-Monitor.ps1 b/scripts/Weekly-Performance-Monitor.ps1 index f0208e5e..f91f2154 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 = 10000, + + [Parameter()] + [ValidateRange(0.01, 100.0)] + [double]$DeadTuplePercentThreshold = 20.0 ) # ============================================================================ @@ -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, tablename) 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