1
0
mirror of https://github.com/Radarr/Radarr.git synced 2024-10-02 14:17:19 +02:00

Cache root folders and improve getting disk space for movie path roots

(cherry picked from commit 63fdf8ca8ff9b22ce4cf8764cc05aad5d1d0ae62)

Closes #10219
This commit is contained in:
Mark McDowall 2024-07-28 16:58:16 -07:00 committed by Bogdan
parent c9f28fdc4f
commit 401e19547c
3 changed files with 55 additions and 24 deletions

View File

@ -6,6 +6,7 @@
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Core.DiskSpace; using NzbDrone.Core.DiskSpace;
using NzbDrone.Core.Movies; using NzbDrone.Core.Movies;
using NzbDrone.Core.RootFolders;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;
@ -16,14 +17,14 @@ public class DiskSpaceServiceFixture : CoreTest<DiskSpaceService>
{ {
private string _moviesFolder; private string _moviesFolder;
private string _moviesFolder2; private string _moviesFolder2;
private string _droneFactoryFolder; private string _rootFolder;
[SetUp] [SetUp]
public void SetUp() public void SetUp()
{ {
_moviesFolder = @"G:\fasdlfsdf\movies".AsOsAgnostic(); _moviesFolder = @"G:\fasdlfsdf\movies".AsOsAgnostic();
_moviesFolder2 = @"G:\fasdlfsdf\movies2".AsOsAgnostic(); _moviesFolder2 = @"G:\fasdlfsdf\movies2".AsOsAgnostic();
_droneFactoryFolder = @"G:\dronefactory".AsOsAgnostic(); _rootFolder = @"G:\fasdlfsdf".AsOsAgnostic();
Mocker.GetMock<IDiskProvider>() Mocker.GetMock<IDiskProvider>()
.Setup(v => v.GetMounts()) .Setup(v => v.GetMounts())
@ -51,6 +52,13 @@ private void GivenMovies(params Movie[] movies)
.Returns(movies.ToDictionary(x => x.Id, x => x.Path)); .Returns(movies.ToDictionary(x => x.Id, x => x.Path));
} }
private void GivenRootFolder(string moviePath, string rootFolderPath)
{
Mocker.GetMock<IRootFolderService>()
.Setup(v => v.GetBestRootFolderPath(moviePath, null))
.Returns(rootFolderPath);
}
private void GivenExistingFolder(string folder) private void GivenExistingFolder(string folder)
{ {
Mocker.GetMock<IDiskProvider>() Mocker.GetMock<IDiskProvider>()
@ -62,8 +70,8 @@ private void GivenExistingFolder(string folder)
public void should_check_diskspace_for_movies_folders() public void should_check_diskspace_for_movies_folders()
{ {
GivenMovies(new Movie { Path = _moviesFolder }); GivenMovies(new Movie { Path = _moviesFolder });
GivenRootFolder(_moviesFolder, _rootFolder);
GivenExistingFolder(_moviesFolder); GivenExistingFolder(_rootFolder);
var freeSpace = Subject.GetFreeSpace(); var freeSpace = Subject.GetFreeSpace();
@ -74,9 +82,9 @@ public void should_check_diskspace_for_movies_folders()
public void should_check_diskspace_for_same_root_folder_only_once() public void should_check_diskspace_for_same_root_folder_only_once()
{ {
GivenMovies(new Movie { Id = 1, Path = _moviesFolder }, new Movie { Id = 2, Path = _moviesFolder2 }); GivenMovies(new Movie { Id = 1, Path = _moviesFolder }, new Movie { Id = 2, Path = _moviesFolder2 });
GivenRootFolder(_moviesFolder, _rootFolder);
GivenExistingFolder(_moviesFolder); GivenRootFolder(_moviesFolder, _rootFolder);
GivenExistingFolder(_moviesFolder2); GivenExistingFolder(_rootFolder);
var freeSpace = Subject.GetFreeSpace(); var freeSpace = Subject.GetFreeSpace();
@ -87,9 +95,10 @@ public void should_check_diskspace_for_same_root_folder_only_once()
} }
[Test] [Test]
public void should_not_check_diskspace_for_missing_movie_folders() public void should_not_check_diskspace_for_missing_movie_root_folders()
{ {
GivenMovies(new Movie { Path = _moviesFolder }); GivenMovies(new Movie { Path = _moviesFolder });
GivenRootFolder(_moviesFolder, _rootFolder);
var freeSpace = Subject.GetFreeSpace(); var freeSpace = Subject.GetFreeSpace();

View File

@ -7,6 +7,7 @@
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Movies; using NzbDrone.Core.Movies;
using NzbDrone.Core.RootFolders;
namespace NzbDrone.Core.DiskSpace namespace NzbDrone.Core.DiskSpace
{ {
@ -18,14 +19,16 @@ public interface IDiskSpaceService
public class DiskSpaceService : IDiskSpaceService public class DiskSpaceService : IDiskSpaceService
{ {
private readonly IMovieService _movieService; private readonly IMovieService _movieService;
private readonly IRootFolderService _rootFolderService;
private readonly IDiskProvider _diskProvider; private readonly IDiskProvider _diskProvider;
private readonly Logger _logger; private readonly Logger _logger;
private static readonly Regex _regexSpecialDrive = new Regex("^/var/lib/(docker|rancher|kubelet)(/|$)|^/(boot|etc)(/|$)|/docker(/var)?/aufs(/|$)", RegexOptions.Compiled); private static readonly Regex _regexSpecialDrive = new Regex("^/var/lib/(docker|rancher|kubelet)(/|$)|^/(boot|etc)(/|$)|/docker(/var)?/aufs(/|$)", RegexOptions.Compiled);
public DiskSpaceService(IMovieService movieService, IDiskProvider diskProvider, Logger logger) public DiskSpaceService(IMovieService movieService, IRootFolderService rootFolderService, IDiskProvider diskProvider, Logger logger)
{ {
_movieService = movieService; _movieService = movieService;
_rootFolderService = rootFolderService;
_diskProvider = diskProvider; _diskProvider = diskProvider;
_logger = logger; _logger = logger;
} }
@ -43,9 +46,15 @@ public List<DiskSpace> GetFreeSpace()
private IEnumerable<string> GetMoviesRootPaths() private IEnumerable<string> GetMoviesRootPaths()
{ {
// Get all movie paths and find the correct root folder for each. For each unique root folder path,
// ensure the path exists and get its path root and return all unique path roots.
return _movieService.AllMoviePaths() return _movieService.AllMoviePaths()
.Where(s => s.Value.IsPathValid(PathValidationType.CurrentOs) && _diskProvider.FolderExists(s.Value)) .Where(s => s.Value.IsPathValid(PathValidationType.CurrentOs))
.Select(s => _diskProvider.GetPathRoot(s.Value)) .Select(s => _rootFolderService.GetBestRootFolderPath(s.Value))
.Distinct()
.Where(r => _diskProvider.FolderExists(r))
.Select(r => _diskProvider.GetPathRoot(r))
.Distinct(); .Distinct();
} }

View File

@ -5,6 +5,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using NLog; using NLog;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Common.Cache;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
@ -32,6 +33,8 @@ public class RootFolderService : IRootFolderService
private readonly INamingConfigService _namingConfigService; private readonly INamingConfigService _namingConfigService;
private readonly Logger _logger; private readonly Logger _logger;
private readonly ICached<string> _cache;
private static readonly HashSet<string> SpecialFolders = new HashSet<string> private static readonly HashSet<string> SpecialFolders = new HashSet<string>
{ {
"$recycle.bin", "$recycle.bin",
@ -50,6 +53,7 @@ public RootFolderService(IRootFolderRepository rootFolderRepository,
IMovieRepository movieRepository, IMovieRepository movieRepository,
IConfigService configService, IConfigService configService,
INamingConfigService namingConfigService, INamingConfigService namingConfigService,
ICacheManager cacheManager,
Logger logger) Logger logger)
{ {
_rootFolderRepository = rootFolderRepository; _rootFolderRepository = rootFolderRepository;
@ -58,6 +62,8 @@ public RootFolderService(IRootFolderRepository rootFolderRepository,
_configService = configService; _configService = configService;
_namingConfigService = namingConfigService; _namingConfigService = namingConfigService;
_logger = logger; _logger = logger;
_cache = cacheManager.GetCache<string>(GetType());
} }
public List<RootFolder> All() public List<RootFolder> All()
@ -115,7 +121,7 @@ public RootFolder Add(RootFolder rootFolder)
if (!_diskProvider.FolderWritable(rootFolder.Path)) if (!_diskProvider.FolderWritable(rootFolder.Path))
{ {
throw new UnauthorizedAccessException(string.Format("Root folder path '{0}' is not writable by user '{1}'", rootFolder.Path, Environment.UserName)); throw new UnauthorizedAccessException($"Root folder path '{rootFolder.Path}' is not writable by user '{Environment.UserName}'");
} }
_rootFolderRepository.Insert(rootFolder); _rootFolderRepository.Insert(rootFolder);
@ -123,6 +129,7 @@ public RootFolder Add(RootFolder rootFolder)
var moviePaths = _movieRepository.AllMoviePaths(); var moviePaths = _movieRepository.AllMoviePaths();
GetDetails(rootFolder, moviePaths, true); GetDetails(rootFolder, moviePaths, true);
_cache.Clear();
return rootFolder; return rootFolder;
} }
@ -130,6 +137,7 @@ public RootFolder Add(RootFolder rootFolder)
public void Remove(int id) public void Remove(int id)
{ {
_rootFolderRepository.Delete(id); _rootFolderRepository.Delete(id);
_cache.Clear();
} }
private List<UnmappedFolder> GetUnmappedFolders(string path, Dictionary<int, string> moviePaths) private List<UnmappedFolder> GetUnmappedFolders(string path, Dictionary<int, string> moviePaths)
@ -201,18 +209,7 @@ public RootFolder Get(int id, bool timeout)
public string GetBestRootFolderPath(string path, List<RootFolder> rootFolders = null) public string GetBestRootFolderPath(string path, List<RootFolder> rootFolders = null)
{ {
var allRootFoldersToConsider = rootFolders ?? All(); return _cache.Get(path, () => GetBestRootFolderPathInternal(path, rootFolders), TimeSpan.FromDays(1));
var possibleRootFolder = allRootFoldersToConsider.Where(r => r.Path.IsParentPath(path)).MaxBy(r => r.Path.Length);
if (possibleRootFolder == null)
{
var osPath = new OsPath(path);
return osPath.Directory.ToString().TrimEnd(osPath.IsUnixPath ? '/' : '\\');
}
return possibleRootFolder?.Path;
} }
private void GetDetails(RootFolder rootFolder, Dictionary<int, string> moviePaths, bool timeout) private void GetDetails(RootFolder rootFolder, Dictionary<int, string> moviePaths, bool timeout)
@ -228,5 +225,21 @@ private void GetDetails(RootFolder rootFolder, Dictionary<int, string> moviePath
} }
}).Wait(timeout ? 5000 : -1); }).Wait(timeout ? 5000 : -1);
} }
private string GetBestRootFolderPathInternal(string path, List<RootFolder> rootFolders = null)
{
var allRootFoldersToConsider = rootFolders ?? All();
var possibleRootFolder = allRootFoldersToConsider.Where(r => r.Path.IsParentPath(path)).MaxBy(r => r.Path.Length);
if (possibleRootFolder == null)
{
var osPath = new OsPath(path);
return osPath.Directory.ToString().TrimEnd(osPath.IsUnixPath ? '/' : '\\');
}
return possibleRootFolder.Path;
}
} }
} }