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:
@@ -13,6 +13,7 @@ using MediaBrowser.Controller.Configuration;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Npgsql;
|
||||
using JellyfinDbProviderFactory = System.Func<System.IServiceProvider, Jellyfin.Database.Implementations.IJellyfinDatabaseProvider>;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Extensions;
|
||||
@@ -28,6 +29,12 @@ public static class ServiceCollectionExtensions
|
||||
yield return typeof(PostgreSqlDatabaseProvider);
|
||||
}
|
||||
|
||||
private static int GetPoolOption(IEnumerable<CustomDatabaseOption>? options, string key, int defaultValue)
|
||||
{
|
||||
var value = options?.FirstOrDefault(o => o.Key.Equals(key, StringComparison.OrdinalIgnoreCase))?.Value;
|
||||
return int.TryParse(value, out var parsed) ? parsed : defaultValue;
|
||||
}
|
||||
|
||||
private static IDictionary<string, JellyfinDbProviderFactory> GetSupportedDbProviders()
|
||||
{
|
||||
var items = new Dictionary<string, JellyfinDbProviderFactory>(StringComparer.InvariantCultureIgnoreCase);
|
||||
@@ -125,6 +132,33 @@ public static class ServiceCollectionExtensions
|
||||
|
||||
serviceCollection.AddSingleton<IJellyfinDatabaseProvider>(providerFactory!);
|
||||
|
||||
if (efCoreConfiguration.DatabaseType.Equals("Jellyfin-PostgreSQL", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
serviceCollection.AddSingleton<NpgsqlDataSource>(static sp =>
|
||||
{
|
||||
var config = sp.GetRequiredService<IServerConfigurationManager>().GetConfiguration<DatabaseConfigurationOptions>("database");
|
||||
var options = config.CustomProviderOptions?.Options;
|
||||
|
||||
var connectionString =
|
||||
Environment.GetEnvironmentVariable("POSTGRES_CONNECTION_STRING")
|
||||
?? options
|
||||
?.FirstOrDefault(o => o.Key.Equals("ConnectionString", StringComparison.OrdinalIgnoreCase))
|
||||
?.Value
|
||||
?? config.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.");
|
||||
|
||||
var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString);
|
||||
|
||||
dataSourceBuilder.ConnectionStringBuilder.MinPoolSize = GetPoolOption(options, "MinPoolSize", 2);
|
||||
dataSourceBuilder.ConnectionStringBuilder.MaxPoolSize = GetPoolOption(options, "MaxPoolSize", 20);
|
||||
dataSourceBuilder.ConnectionStringBuilder.CommandTimeout = GetPoolOption(options, "CommandTimeout", 30);
|
||||
|
||||
return dataSourceBuilder.Build();
|
||||
});
|
||||
}
|
||||
|
||||
switch (efCoreConfiguration.LockingBehavior)
|
||||
{
|
||||
case DatabaseLockingBehaviorTypes.NoLock:
|
||||
|
||||
+13
-12
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
+12
-2
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user