Wire NpgsqlDataSource pool into DI for PostgreSQL provider (#10)

* Initial plan

* Add NpgsqlDataSource pool wiring to DI (Issue 1.4)"

- PostgreSqlDatabaseProvider: accept NpgsqlDataSource via constructor injection, use it in Initialise()
- PostgreSqlDesignTimeJellyfinDbFactory: build NpgsqlDataSource from connection string for design-time use
- ServiceCollectionExtensions: register NpgsqlDataSource as singleton with pool params (MinPoolSize=2, MaxPoolSize=20, CommandTimeout=30) from CustomProviderOptions.Options

Co-authored-by: ZoltyMat <177592743+ZoltyMat@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: ZoltyMat <177592743+ZoltyMat@users.noreply.github.com>
This commit is contained in:
Copilot
2026-03-04 20:31:04 -05:00
committed by mat
parent 3bc45ff92d
commit a0c38131c8
3 changed files with 59 additions and 14 deletions
@@ -1,11 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Database.Implementations;
using Jellyfin.Database.Implementations.DbConfiguration;
using Microsoft.EntityFrameworkCore;
using Npgsql;
namespace Jellyfin.Database.Providers.PostgreSQL;
@@ -18,24 +18,25 @@ public sealed class PostgreSqlDatabaseProvider : IJellyfinDatabaseProvider
private const string BackupNotSupportedMessage =
"Automated migration backups are not supported for PostgreSQL. Use the jellyfin-pg-backup CronJob for nightly S3 backups.";
private readonly NpgsqlDataSource _dataSource;
/// <summary>
/// Initializes a new instance of the <see cref="PostgreSqlDatabaseProvider"/> class.
/// </summary>
/// <param name="dataSource">The <see cref="NpgsqlDataSource"/> used for PostgreSQL connections.</param>
public PostgreSqlDatabaseProvider(NpgsqlDataSource dataSource)
{
_dataSource = dataSource;
}
/// <inheritdoc/>
public IDbContextFactory<JellyfinDbContext>? DbContextFactory { get; set; }
/// <inheritdoc/>
public void Initialise(DbContextOptionsBuilder options, DatabaseConfigurationOptions databaseConfiguration)
{
var connectionString =
Environment.GetEnvironmentVariable("POSTGRES_CONNECTION_STRING")
?? databaseConfiguration.CustomProviderOptions?.Options
?.FirstOrDefault(o => o.Key.Equals("ConnectionString", StringComparison.OrdinalIgnoreCase))
?.Value
?? databaseConfiguration.CustomProviderOptions?.ConnectionString
?? throw new InvalidOperationException(
"No PostgreSQL connection string found. Set the POSTGRES_CONNECTION_STRING environment variable, " +
"or provide it via CustomProviderOptions.Options[\"ConnectionString\"] or CustomProviderOptions.ConnectionString.");
options.UseNpgsql(
connectionString,
_dataSource,
o => o.MigrationsAssembly(GetType().Assembly.FullName));
}
@@ -4,6 +4,7 @@ using Jellyfin.Database.Implementations.Locking;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Logging.Abstractions;
using Npgsql;
namespace Jellyfin.Database.Providers.PostgreSQL;
@@ -21,12 +22,21 @@ internal sealed class PostgreSqlDesignTimeJellyfinDbFactory : IDesignTimeDbConte
?? "Host=localhost;Database=jellyfin;Username=postgres;Password=postgres";
var optionsBuilder = new DbContextOptionsBuilder<JellyfinDbContext>();
optionsBuilder.UseNpgsql(connectionString, o => o.MigrationsAssembly(GetType().Assembly));
// Build a NpgsqlDataSource for EF Core configuration. The DI-owned singleton data source
// is not available in design-time context; this instance is intentionally not disposed here
// because EF Core holds a reference to it for the lifetime of the returned context.
// As a design-time-only factory (used only for dotnet-ef CLI operations), the process
// exits after the migration is applied, which releases all resources.
#pragma warning disable CA2000 // Dispose objects before losing scope
var dataSource = new NpgsqlDataSourceBuilder(connectionString).Build();
#pragma warning restore CA2000 // Dispose objects before losing scope
optionsBuilder.UseNpgsql(dataSource, o => o.MigrationsAssembly(GetType().Assembly));
return new JellyfinDbContext(
optionsBuilder.Options,
NullLogger<JellyfinDbContext>.Instance,
new PostgreSqlDatabaseProvider(),
new PostgreSqlDatabaseProvider(dataSource),
new NoLockBehavior(NullLogger<NoLockBehavior>.Instance));
}
}