Ombi
Ombi copied to clipboard
Adding postgres support to ombi
I saw the feature suggestion at https://features.ombi.io/suggestions/127609/add-postgresql-as-a-database-provider and thought It would be nice and quick to implement, as the *arrs all have postgres support but not mySql support built in.
This should be pretty straightforward - I added NpgSql and generated the migrations for it, then updated DatabaseExtensions.cs
.
I've also built a docker image that includes this, based on the LinuxServer Ombi container at ghcr.io/dben/ombi:latest
.
In my personal testing deploying the image in my nomad cluster, this appears to work identically to the mysql provider, at least for my setup (plex/radarr/sonarr/email notifications). Since it's just EF I woudn't expect too much divergence.
After further testing, I made a few updates:
- Fixed an issue where Npgsql was more strict than the app in timezone comparison, causing some failed inserts
- Since Ombi has a few places where it uses raw SQL queries, I switched the postgres tables to be case-insensitive by converting identifiers to lowercase in the postgres migrations. This lets us avoid needing quotes for everything too.
- I noticed a few unit tests were failng from midnght UTC to about 2-4 am UTC - I switched the
addHours
calls toaddMinutes
so there's only a down time of a few minutes instead.
Just chiming in to note, I've been testing this for ~5 weeks now, all without issue - going through the request history, it looks like users requested 11 series and 14 movies in that time (normal usage).
In an attempt to 'stress' the app, I'd added 4 multi-season TV series (~430 episodes combined) along with 8 movies, all in rapid succession; all went without a hitch, and was still more performant than adding a single series with the sqlite DB, at least to my eyes! 😮
Great work @dben !
Thanks for the testing - I've been running this PR as well on my personal instance since December, and haven't had any issues, although I've only had about a half dozen new movies and seven shortish tv shows added during that time.
could you provide a working example for database.json, it will also be easier to update the docs later based on this
could you provide a working example for database.json, it will also be easier to update the docs later based on this
sure, here's what I use (with the passwords redacted) - you can use separate databases too, but they all work lumped together:
{
"OmbiDatabase": {
"Type": "Postgres",
"ConnectionString": "Host=127.0.0.1;Port=5432;Database=ombi;Username=postgres;Password=password"
},
"SettingsDatabase": {
"Type": "Postgres",
"ConnectionString": "Host=127.0.0.1;Port=5432;Database=ombi;Username=postgres;Password=password"
},
"ExternalDatabase": {
"Type": "Postgres",
"ConnectionString": "Host=127.0.0.1;Port=5432;Database=ombi;Username=postgres;Password=password"
}
Considering people have been using this, i'll get this merged in soon
@dben I'm getting the following exception trying to launch with the Postgres connection. I exported the *.db files and imported the tables and content into Postgres. Created the database.json file and put it in the /opt/Ombi directory.
I'm running 4.44.1 on Ubuntu 23.04. This is the startup command I'm using:
/opt/Ombi/Ombi --storage /opt/nas/media_server/Ombi/
Database.json:
{
"OmbiDatabase": {
"Type": "Postgres",
"ConnectionString": "Host=database.redacted.domain;Port=5432;Database=ombi;Username=ombi;Password=Redacted"
},
"SettingsDatabase": {
"Type": "Postgres",
"ConnectionString": "Host=database.redacted.domain;Port=5432;Database=ombi;Username=ombi;Password=Redacted"
},
"ExternalDatabase": {
"Type": "Postgres",
"ConnectionString": "Host=database.redacted.domain;Port=5432;Database=ombi;Username=ombi;Password=Redacted"
}
}
The Exception and Crash:
Unhandled exception. System.TypeLoadException: Could not load type 'Microsoft.EntityFrameworkCore.Storage.IRelationalValueBufferFactoryFactory' from assembly 'Microsoft.EntityFrameworkCore.Relational, Version=8.0.5.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.
at Microsoft.Extensions.DependencyInjection.NpgsqlServiceCollectionExtensions.AddEntityFrameworkNpgsql(IServiceCollection serviceCollection)
at Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal.NpgsqlOptionsExtension.ApplyServices(IServiceCollection services)
at Microsoft.EntityFrameworkCore.Internal.ServiceProviderCache.ApplyServices(IDbContextOptions options, ServiceCollection services)
at Microsoft.EntityFrameworkCore.Internal.ServiceProviderCache.<GetOrAdd>g__BuildServiceProvider|4_1(IDbContextOptions _, ValueTuple`2 arguments)
at Microsoft.EntityFrameworkCore.Internal.ServiceProviderCache.<>c.<GetOrAdd>b__4_0(IDbContextOptions contextOptions, ValueTuple`2 tuples)
at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd[TArg](TKey key, Func`3 valueFactory, TArg factoryArgument)
at Microsoft.EntityFrameworkCore.Internal.ServiceProviderCache.GetOrAdd(IDbContextOptions options, Boolean providerRequired)
at Microsoft.EntityFrameworkCore.DbContext..ctor(DbContextOptions options)
at Ombi.Store.Context.SettingsContext..ctor(DbContextOptions options) in /home/runner/work/Ombi/Ombi/src/Ombi.Store/Context/SettingsContext.cs:line 22
at Ombi.Store.Context.Postgres.SettingsPostgresContext..ctor(DbContextOptions`1 options) in /home/runner/work/Ombi/Ombi/src/Ombi.Store/Context/Postgres/SettingsPostgresContext.cs:line 8
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
at System.Reflection.MethodBaseInvoker.InvokeWithOneArg(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass2_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
at Ombi.Program.Main(String[] args) in /home/runner/work/Ombi/Ombi/src/Ombi/Program.cs:line 74
at Ombi.Program.<Main>(String[] args)