Postgres perf, path migration, and shutdown race fixes

- Major query performance boost: replaced correlated subqueries with DistinctBy() in BaseItemRepository, added episode deduplication index, and increased default command timeout to 120s.
- Fixed LibraryMonitor shutdown race: added disposal checks and exception handling in FileRefresher to prevent ObjectDisposedException.
- Added PowerShell and SQL utilities for fixing Linux paths in config files and database; new scripts for WebDir and path migration.
- Auto-fix for WebDir in startup.json on load; new helper scripts and docs.
- Added automated weekly DB performance monitoring scripts and reporting.
- Expanded documentation and README for all fixes, scripts, and migration steps.
- Updated SQL scripts for index creation and diagnostics; improved output handling.
- Updated db-config defaults and added new diagnostic/report files.
This commit is contained in:
2026-03-05 08:56:42 -05:00
parent 0eb44818ff
commit 7eb2b445cb
44 changed files with 3504 additions and 23 deletions
+143
View File
@@ -0,0 +1,143 @@
-- ============================================================================
-- Optimize Indexes for Peoples and ItemValues Tables
-- Generated: 2026-03-05
-- Purpose: Reduce sequential scans on high-traffic tables
-- ============================================================================
\pset pager off
\echo '========================================='
\echo 'Optimizing Peoples and ItemValues Indexes'
\echo '========================================='
\echo ''
-- ============================================================================
-- 1. PEOPLES TABLE OPTIMIZATION
-- ============================================================================
-- Current Issue: 144,299 sequential scans reading 13.7 BILLION rows
-- Current Indexes: IX_Peoples_Name, PK_Peoples
-- Target: Reduce sequential scans by 80%+
\echo '1. Creating Peoples Table Indexes...'
\echo '-----------------------------------------'
-- Index for queries filtering by Name (case-insensitive searches)
-- Supports ILIKE/LIKE queries
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_peoples_name_lower
ON library."Peoples" (LOWER("Name"))
WHERE "Name" IS NOT NULL;
-- Composite index for common join patterns (People -> Items)
-- Supports queries that need both Id and Name
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_peoples_id_name
ON library."Peoples" ("Id", "Name")
WHERE "Name" IS NOT NULL;
-- Index for external ID lookups (TMDB, IMDB, etc.)
-- Supports metadata provider queries
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_peoples_providerid
ON library."Peoples" ("ProviderIds")
WHERE "ProviderIds" IS NOT NULL;
\echo 'Peoples indexes created successfully!'
\echo ''
-- ============================================================================
-- 2. ITEMVALUES TABLE OPTIMIZATION
-- ============================================================================
-- Current Issue: 506,048 sequential scans reading 3.7 BILLION rows
-- Current Indexes: IX_ItemValues_Type_CleanValue, IX_ItemValues_Type_Value
-- Target: Increase index usage from 82% to 95%+
\echo '2. Creating ItemValues Table Indexes...'
\echo '-----------------------------------------'
-- Index for CleanValue lookups without Type filter
-- Supports genre/tag searches that don't filter by type
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_itemvalues_cleanvalue
ON library."ItemValues" ("CleanValue")
WHERE "CleanValue" IS NOT NULL;
-- Index for Value lookups (with original casing)
-- Supports exact value matches
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_itemvalues_value
ON library."ItemValues" ("Value")
WHERE "Value" IS NOT NULL;
-- Composite index for ItemValuesMap joins
-- Optimizes: ItemValues JOIN ItemValuesMap ON ItemValues.Id = ItemValuesMap.ItemValueId
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_itemvalues_id_type_cleanvalue
ON library."ItemValues" ("Id", "Type", "CleanValue");
-- Index for reverse lookups (find all items with specific values)
-- Supports queries like "find all genres" or "find all tags"
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_itemvalues_type_id
ON library."ItemValues" ("Type", "Id")
WHERE "Type" IS NOT NULL;
-- Partial index for common value types (Genre, Tags, Studio, etc.)
-- Reduces index size while covering most common queries
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_itemvalues_common_types
ON library."ItemValues" ("Type", "CleanValue", "Id")
WHERE "Type" IN (0, 1, 2, 3, 4, 5, 6, 7, 8); -- Common metadata types
\echo 'ItemValues indexes created successfully!'
\echo ''
-- ============================================================================
-- 3. UPDATE STATISTICS
-- ============================================================================
\echo '3. Updating Table Statistics...'
\echo '-----------------------------------------'
-- Update statistics so query planner knows about new indexes
ANALYZE VERBOSE library."Peoples";
ANALYZE VERBOSE library."ItemValues";
\echo ''
\echo '========================================='
\echo 'Optimization Complete!'
\echo '========================================='
\echo ''
-- ============================================================================
-- 4. VERIFY NEW INDEXES
-- ============================================================================
\echo '4. Verifying New Indexes...'
\echo '-----------------------------------------'
-- Show all indexes on Peoples table
\echo ''
\echo 'Peoples Table Indexes:'
SELECT
schemaname,
tablename,
indexname,
pg_size_pretty(pg_relation_size(indexrelid)) AS index_size
FROM pg_stat_user_indexes
WHERE schemaname = 'library'
AND tablename = 'Peoples'
ORDER BY indexname;
\echo ''
\echo 'ItemValues Table Indexes:'
-- Show all indexes on ItemValues table
SELECT
schemaname,
tablename,
indexname,
pg_size_pretty(pg_relation_size(indexrelid)) AS index_size
FROM pg_stat_user_indexes
WHERE schemaname = 'library'
AND tablename = 'ItemValues'
ORDER BY indexname;
\echo ''
\echo '========================================='
\echo 'Next Steps:'
\echo '1. Use Jellyfin normally for a few hours'
\echo '2. Run: sql\diagnostics.sql'
\echo '3. Check if sequential scans decreased'
\echo '4. Run: sql\query-analysis.sql for slow query details'
\echo '========================================='