Limit cache size again

Co-Authored-By: Cody Robibero <cody@robibe.ro>
This commit is contained in:
Shadowghost
2026-09-05 07:08:14 +02:00
parent 46dd7d8e99
commit e5cd3381ac
3 changed files with 152 additions and 15 deletions
@@ -1,3 +1,4 @@
using System.Globalization;
using System.IO;
using System.Linq;
using MediaBrowser.Controller.Providers;
@@ -326,6 +327,78 @@ namespace Jellyfin.Controller.Tests
fileSystemMock.Verify(f => f.GetFileSystemEntries(parentPath), Times.Once);
}
[Fact]
public void GetFileSystemEntries_MoreRecordsThanTheCeiling_DropsCache()
{
// Charged by the files in a listing, not the number of listings, so a few big folders
// reach the limit where a lot of small ones would not.
const int FolderCount = 60;
var bigListing = new FileSystemMetadata[5000];
for (var i = 0; i < bigListing.Length; i++)
{
bigListing[i] = new FileSystemMetadata
{
FullName = "/music/track" + i.ToString(CultureInfo.InvariantCulture),
IsDirectory = false
};
}
var fileSystemMock = new Mock<IFileSystem>();
fileSystemMock.Setup(f => f.GetFileSystemEntries(It.IsAny<string>()))
.Returns(bigListing);
var directoryService = new DirectoryService(fileSystemMock.Object);
const string FirstPath = "/music/artist0";
directoryService.GetFileSystemEntries(FirstPath);
for (var i = 1; i < FolderCount; i++)
{
directoryService.GetFileSystemEntries("/music/artist" + i.ToString(CultureInfo.InvariantCulture));
}
directoryService.GetFileSystemEntries(FirstPath);
fileSystemMock.Verify(f => f.GetFileSystemEntries(FirstPath), Times.Exactly(2));
}
[Fact]
public void GetFileSystemEntries_RepeatedlyInvalidatedFolder_KeepsUnrelatedEntriesCached()
{
// Invalidating gives the records back, so churning one folder must not add up to the
// ceiling and drop everything else with it.
const int ChurnCount = 50;
var bigListing = new FileSystemMetadata[5000];
for (var i = 0; i < bigListing.Length; i++)
{
bigListing[i] = new FileSystemMetadata
{
FullName = "/music/track" + i.ToString(CultureInfo.InvariantCulture),
IsDirectory = false
};
}
var fileSystemMock = new Mock<IFileSystem>();
fileSystemMock.Setup(f => f.GetFileSystemEntries(It.IsAny<string>()))
.Returns(bigListing);
var directoryService = new DirectoryService(fileSystemMock.Object);
const string ChurnedPath = "/music/watched";
const string StablePath = "/music/untouched";
directoryService.GetFileSystemEntries(StablePath);
for (var i = 0; i < ChurnCount; i++)
{
directoryService.GetFileSystemEntries(ChurnedPath);
directoryService.Invalidate(ChurnedPath);
}
directoryService.GetFileSystemEntries(StablePath);
fileSystemMock.Verify(f => f.GetFileSystemEntries(StablePath), Times.Once);
}
[Fact]
public void GetFileSystemEntry_MissingPath_IsNotRemembered()
{
@@ -65,6 +65,7 @@ namespace Jellyfin.Controller.Tests.LibraryTaskScheduler
/// so a shutdown has to reach them. It does not travel from the linked source back to the one
/// the cleanup cancels, which is what made them immortal.
/// </summary>
/// <returns><placeholder>A <see cref="Task"/> representing the asynchronous unit test.</placeholder></returns>
[Fact]
public async Task ApplicationStopping_RetiresRunners()
{
@@ -87,6 +88,7 @@ namespace Jellyfin.Controller.Tests.LibraryTaskScheduler
/// The cleanup used to be a one shot: it never released the scheduling slot it took, so
/// every runner spawned after the first pass stayed around for the lifetime of the server.
/// </summary>
/// <returns><placeholder>A <see cref="Task"/> representing the asynchronous unit test.</placeholder></returns>
[Fact]
public async Task Enqueue_RetiresIdleRunnersAfterEveryOperation()
{
@@ -108,6 +110,7 @@ namespace Jellyfin.Controller.Tests.LibraryTaskScheduler
/// Disposing used to sit out the rest of the cleanup grace period, holding up shutdown for
/// up to a minute.
/// </summary>
/// <returns><placeholder>A <see cref="Task"/> representing the asynchronous unit test.</placeholder></returns>
[Fact]
public async Task DisposeAsync_DoesNotWaitOutTheGracePeriod()
{