using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Jellyfin.Database.Implementations; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.ScheduledTasks.Tasks; /// /// Optimizes Jellyfin's database by issuing a VACUUM command. /// public class OptimizeDatabaseTask : IScheduledTask, IConfigurableScheduledTask { private readonly ILogger _logger; private readonly ILocalizationManager _localization; private readonly IJellyfinDatabaseProvider _jellyfinDatabaseProvider; private readonly ILibraryManager _libraryManager; /// /// Initializes a new instance of the class. /// /// Instance of the interface. /// Instance of the interface. /// Instance of the JellyfinDatabaseProvider that can be used for provider specific operations. /// Instance of the interface. public OptimizeDatabaseTask( ILogger logger, ILocalizationManager localization, IJellyfinDatabaseProvider jellyfinDatabaseProvider, ILibraryManager libraryManager) { _logger = logger; _localization = localization; _jellyfinDatabaseProvider = jellyfinDatabaseProvider; _libraryManager = libraryManager; } /// public string Name => _localization.GetLocalizedString("TaskOptimizeDatabase"); /// public string Description => _localization.GetLocalizedString("TaskOptimizeDatabaseDescription"); /// public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory"); /// public string Key => "OptimizeDatabaseTask"; /// public bool IsHidden => false; /// public bool IsEnabled => true; /// public bool IsLogged => true; /// public IEnumerable GetDefaultTriggers() { yield return new TaskTriggerInfo { Type = TaskTriggerInfoType.IntervalTrigger, IntervalTicks = TimeSpan.FromHours(6).Ticks }; } /// public async Task ExecuteAsync(IProgress progress, CancellationToken cancellationToken) { // Vacuuming/checkpointing requires an exclusive lock on the database. Running it while a library scan is in // progress causes both operations to contend for the database and can stall the scan, so defer optimization // until no scan is running. The task will run again on its next trigger. if (_libraryManager.IsScanRunning) { _logger.LogInformation("Skipping database optimization because a library scan is currently running."); return; } _logger.LogInformation("Optimizing and vacuuming jellyfin.db..."); try { await _jellyfinDatabaseProvider.RunScheduledOptimisation(cancellationToken).ConfigureAwait(false); } catch (Exception e) { _logger.LogError(e, "Error while optimizing jellyfin.db"); } } }