yes-- Jellyfin index cleanup plan (conservative, phased)
-- Generated: 2026-03-08
-- Notes:
-- 1) This script does NOT run automatically; execute sections manually.
-- 2) Use DROP INDEX CONCURRENTLY to avoid long blocking.
-- 3) Do not run inside an explicit transaction block.
-- 4) Re-check stats after each phase before continuing.

/* -------------------------------------------------------------------------
   Baseline: capture current index usage before any change
--------------------------------------------------------------------------- */
SELECT
	now() AS captured_at,
	schemaname,
	relname AS table_name,
	indexrelname AS index_name,
	idx_scan,
	idx_tup_read,
	idx_tup_fetch,
	pg_size_pretty(pg_relation_size(indexrelid)) AS index_size
FROM pg_stat_user_indexes
WHERE schemaname IN ('library', 'authentication', 'users', 'activitylog', 'displaypreferences')
ORDER BY idx_scan ASC, pg_relation_size(indexrelid) DESC;

/* -------------------------------------------------------------------------
   Phase 1 (lowest risk): redundant MediaStreamInfos non-unique indexes
   Rationale: all 0 scans since last stats reset, strong overlap among keys.
--------------------------------------------------------------------------- */
-- Run one statement at a time and observe workload/latency between statements.
DROP INDEX CONCURRENTLY IF EXISTS library."IX_MediaStreamInfos_StreamIndex_StreamType_Language";
DROP INDEX CONCURRENTLY IF EXISTS library."IX_MediaStreamInfos_StreamIndex_StreamType";
DROP INDEX CONCURRENTLY IF EXISTS library."IX_MediaStreamInfos_StreamIndex";

/* -------------------------------------------------------------------------
   Verification checkpoint after Phase 1
--------------------------------------------------------------------------- */
SELECT
	now() AS check_time,
	schemaname,
	relname AS table_name,
	indexrelname AS index_name,
	idx_scan,
	idx_tup_read,
	idx_tup_fetch,
	pg_size_pretty(pg_relation_size(indexrelid)) AS index_size
FROM pg_stat_user_indexes
WHERE schemaname = 'library'
	AND relname = 'MediaStreamInfos'
ORDER BY idx_scan ASC, pg_relation_size(indexrelid) DESC;

/* -------------------------------------------------------------------------
   Phase 2 (medium risk): overlapping BaseItemProviders helper indexes
   Rationale: all 0 scans since last stats reset; candidates may be redundant
   with PK and alternative access paths, but monitor query plans after each.
--------------------------------------------------------------------------- */
DROP INDEX CONCURRENTLY IF EXISTS library.baseitemproviders_providerid_idx;
DROP INDEX CONCURRENTLY IF EXISTS library.baseitemproviders_providervalue_idx;

-- Optional in Phase 2b (only if workload remains healthy after Phase 2):
-- DROP INDEX CONCURRENTLY IF EXISTS library."IX_BaseItemProviders_ProviderId_ProviderValue_ItemId";

/* -------------------------------------------------------------------------
   Final review query: confirm remaining low-scan indexes
--------------------------------------------------------------------------- */
SELECT
	schemaname,
	relname AS table_name,
	indexrelname AS index_name,
	idx_scan,
	pg_size_pretty(pg_relation_size(indexrelid)) AS index_size,
	pg_get_indexdef(indexrelid) AS index_definition
FROM pg_stat_user_indexes
WHERE schemaname IN ('library', 'authentication', 'users', 'activitylog', 'displaypreferences')
ORDER BY idx_scan ASC, pg_relation_size(indexrelid) DESC;

/* -------------------------------------------------------------------------
   Explicitly excluded from removal in this plan:
   - Unique and PK-backed indexes (constraint critical)
   - IX_Users_Username
   - IX_DeviceOptions_DeviceId
--------------------------------------------------------------------------- */