< 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
48%
Covered lines: 15
Uncovered lines: 16
Coverable lines: 31
Total lines: 128
Line coverage: 48.3%
Branch coverage
0%
Covered branches: 0
Total branches: 4
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(...)0%620%
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    {
 2232        _applicationPaths = applicationPaths;
 2233        _logger = logger;
 2234    }
 35
 36    /// <inheritdoc/>
 37    public IDbContextFactory<JellyfinDbContext>? DbContextFactory { get; set; }
 38
 39    /// <inheritdoc/>
 40    public void Initialise(DbContextOptionsBuilder options)
 41    {
 2142        options
 2143            .UseSqlite(
 2144                $"Filename={Path.Combine(_applicationPaths.DataPath, "jellyfin.db")};Pooling=false",
 2145                sqLiteOptions => sqLiteOptions.MigrationsAssembly(GetType().Assembly))
 2146            // TODO: Remove when https://github.com/dotnet/efcore/pull/35873 is merged & released
 2147            .ConfigureWarnings(warnings =>
 2148                warnings.Ignore(RelationalEventId.NonTransactionalMigrationOperationWarning));
 2149    }
 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        // Run before disposing the application
 80        var context = await DbContextFactory!.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
 81        await using (context.ConfigureAwait(false))
 82        {
 83            await context.Database.ExecuteSqlRawAsync("PRAGMA optimize", cancellationToken).ConfigureAwait(false);
 84        }
 85
 86        SqliteConnection.ClearAllPools();
 87    }
 88
 89    /// <inheritdoc/>
 90    public void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
 91    {
 292        configurationBuilder.Conventions.Add(_ => new DoNotUseReturningClauseConvention());
 293    }
 94
 95    /// <inheritdoc />
 96    public Task<string> MigrationBackupFast(CancellationToken cancellationToken)
 97    {
 098        var key = DateTime.UtcNow.ToString("yyyyMMddhhmmss", CultureInfo.InvariantCulture);
 099        var path = Path.Combine(_applicationPaths.DataPath, "jellyfin.db");
 0100        var backupFile = Path.Combine(_applicationPaths.DataPath, BackupFolderName);
 0101        if (!Directory.Exists(backupFile))
 102        {
 0103            Directory.CreateDirectory(backupFile);
 104        }
 105
 0106        backupFile = Path.Combine(backupFile, $"{key}_jellyfin.db");
 0107        File.Copy(path, backupFile);
 0108        return Task.FromResult(key);
 109    }
 110
 111    /// <inheritdoc />
 112    public Task RestoreBackupFast(string key, CancellationToken cancellationToken)
 113    {
 114        // ensure there are absolutly no dangling Sqlite connections.
 0115        SqliteConnection.ClearAllPools();
 0116        var path = Path.Combine(_applicationPaths.DataPath, "jellyfin.db");
 0117        var backupFile = Path.Combine(_applicationPaths.DataPath, BackupFolderName, $"{key}_jellyfin.db");
 118
 0119        if (!File.Exists(backupFile))
 120        {
 0121            _logger.LogCritical("Tried to restore a backup that does not exist.");
 0122            return Task.CompletedTask;
 123        }
 124
 0125        File.Copy(backupFile, path, true);
 0126        return Task.CompletedTask;
 127    }
 128}