< Summary - Jellyfin

Information
Class: Jellyfin.Database.Providers.Sqlite.SqliteDatabaseProvider
Assembly: Jellyfin.Database.Providers.Sqlite
File(s): /srv/git/jellyfin/src/Jellyfin.Database/Jellyfin.Database.Providers.Sqlite/SqliteDatabaseProvider.cs
Line coverage
50%
Covered lines: 15
Uncovered lines: 15
Coverable lines: 30
Total lines: 130
Line coverage: 50%
Branch coverage
0%
Covered branches: 0
Total branches: 2
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
Initialise(...)100%11100%
OnModelCreating(...)100%11100%
ConfigureConventions(...)100%11100%
MigrationBackupFast(...)100%210%
RestoreBackupFast(...)0%620%

File(s)

/srv/git/jellyfin/src/Jellyfin.Database/Jellyfin.Database.Providers.Sqlite/SqliteDatabaseProvider.cs

#LineLine coverage
 1using System;
 2using System.Globalization;
 3using System.IO;
 4using System.Threading;
 5using System.Threading.Tasks;
 6using Jellyfin.Database.Implementations;
 7using MediaBrowser.Common.Configuration;
 8using Microsoft.Data.Sqlite;
 9using Microsoft.EntityFrameworkCore;
 10using Microsoft.EntityFrameworkCore.Diagnostics;
 11using Microsoft.Extensions.Logging;
 12
 13namespace Jellyfin.Database.Providers.Sqlite;
 14
 15/// <summary>
 16/// Configures jellyfin to use an SQLite database.
 17/// </summary>
 18[JellyfinDatabaseProviderKey("Jellyfin-SQLite")]
 19public sealed class SqliteDatabaseProvider : IJellyfinDatabaseProvider
 20{
 21    private const string BackupFolderName = "SQLiteBackups";
 22    private readonly IApplicationPaths _applicationPaths;
 23    private readonly ILogger<SqliteDatabaseProvider> _logger;
 24
 25    /// <summary>
 26    /// Initializes a new instance of the <see cref="SqliteDatabaseProvider"/> class.
 27    /// </summary>
 28    /// <param name="applicationPaths">Service to construct the fallback when the old data path configuration is used.</
 29    /// <param name="logger">A logger.</param>
 30    public SqliteDatabaseProvider(IApplicationPaths applicationPaths, ILogger<SqliteDatabaseProvider> logger)
 31    {
 4332        _applicationPaths = applicationPaths;
 4333        _logger = logger;
 4334    }
 35
 36    /// <inheritdoc/>
 37    public IDbContextFactory<JellyfinDbContext>? DbContextFactory { get; set; }
 38
 39    /// <inheritdoc/>
 40    public void Initialise(DbContextOptionsBuilder options)
 41    {
 4242        options
 4243            .UseSqlite(
 4244                $"Filename={Path.Combine(_applicationPaths.DataPath, "jellyfin.db")};Pooling=false",
 4245                sqLiteOptions => sqLiteOptions.MigrationsAssembly(GetType().Assembly))
 4246            // TODO: Remove when https://github.com/dotnet/efcore/pull/35873 is merged & released
 4247            .ConfigureWarnings(warnings =>
 4248                warnings.Ignore(RelationalEventId.NonTransactionalMigrationOperationWarning));
 4249    }
 50
 51    /// <inheritdoc/>
 52    public async Task RunScheduledOptimisation(CancellationToken cancellationToken)
 53    {
 54        var context = await DbContextFactory!.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
 55        await using (context.ConfigureAwait(false))
 56        {
 57            if (context.Database.IsSqlite())
 58            {
 59                await context.Database.ExecuteSqlRawAsync("PRAGMA optimize", cancellationToken).ConfigureAwait(false);
 60                await context.Database.ExecuteSqlRawAsync("VACUUM", cancellationToken).ConfigureAwait(false);
 61                _logger.LogInformation("jellyfin.db optimized successfully!");
 62            }
 63            else
 64            {
 65                _logger.LogInformation("This database doesn't support optimization");
 66            }
 67        }
 68    }
 69
 70    /// <inheritdoc/>
 71    public void OnModelCreating(ModelBuilder modelBuilder)
 72    {
 273        modelBuilder.SetDefaultDateTimeKind(DateTimeKind.Utc);
 274    }
 75
 76    /// <inheritdoc/>
 77    public async Task RunShutdownTask(CancellationToken cancellationToken)
 78    {
 79        if (DbContextFactory is null)
 80        {
 81            return;
 82        }
 83
 84        // Run before disposing the application
 85        var context = await DbContextFactory!.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
 86        await using (context.ConfigureAwait(false))
 87        {
 88            await context.Database.ExecuteSqlRawAsync("PRAGMA optimize", cancellationToken).ConfigureAwait(false);
 89        }
 90
 91        SqliteConnection.ClearAllPools();
 92    }
 93
 94    /// <inheritdoc/>
 95    public void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
 96    {
 297        configurationBuilder.Conventions.Add(_ => new DoNotUseReturningClauseConvention());
 298    }
 99
 100    /// <inheritdoc />
 101    public Task<string> MigrationBackupFast(CancellationToken cancellationToken)
 102    {
 0103        var key = DateTime.UtcNow.ToString("yyyyMMddhhmmss", CultureInfo.InvariantCulture);
 0104        var path = Path.Combine(_applicationPaths.DataPath, "jellyfin.db");
 0105        var backupFile = Path.Combine(_applicationPaths.DataPath, BackupFolderName);
 0106        Directory.CreateDirectory(backupFile);
 107
 0108        backupFile = Path.Combine(backupFile, $"{key}_jellyfin.db");
 0109        File.Copy(path, backupFile);
 0110        return Task.FromResult(key);
 111    }
 112
 113    /// <inheritdoc />
 114    public Task RestoreBackupFast(string key, CancellationToken cancellationToken)
 115    {
 116        // ensure there are absolutly no dangling Sqlite connections.
 0117        SqliteConnection.ClearAllPools();
 0118        var path = Path.Combine(_applicationPaths.DataPath, "jellyfin.db");
 0119        var backupFile = Path.Combine(_applicationPaths.DataPath, BackupFolderName, $"{key}_jellyfin.db");
 120
 0121        if (!File.Exists(backupFile))
 122        {
 0123            _logger.LogCritical("Tried to restore a backup that does not exist: {Key}", key);
 0124            return Task.CompletedTask;
 125        }
 126
 0127        File.Copy(backupFile, path, true);
 0128        return Task.CompletedTask;
 129    }
 130}