Merge pull request #17576 from obiwantoby/perf/batch-mediasourcecount-dto
Bugfix: #17547 | Batching MediaSourceCount into one call
This commit is contained in:
@@ -9,6 +9,7 @@ using MediaBrowser.Controller.Drawing;
|
||||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
@@ -205,6 +206,84 @@ public class DtoServiceImageInheritanceTests
|
||||
libraryManager.Verify(x => x.GetPeople(It.IsAny<BaseItem>()), Times.Never);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetBaseItemDtos_Videos_ResolveMediaSourceCountFromBatch_WithoutPerItemLookup()
|
||||
{
|
||||
static Movie MakeMovie() => new Movie
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = "Movie",
|
||||
ImageInfos = []
|
||||
};
|
||||
|
||||
var movieOne = MakeMovie();
|
||||
var movieTwo = MakeMovie();
|
||||
|
||||
var libraryManager = new Mock<ILibraryManager>();
|
||||
|
||||
// DtoService detects which videos own alternate versions in ONE batch
|
||||
// (GetItemIdsWithAlternateVersions) before the per-item loop. Videos absent from that set have a
|
||||
// single media source, so the per-item GetLinkedAlternateVersions/GetLocalAlternateVersionIds
|
||||
// queries (the N+1) must be skipped entirely. Here neither movie has alternate versions.
|
||||
libraryManager
|
||||
.Setup(x => x.GetItemIdsWithAlternateVersions(It.IsAny<IReadOnlyList<Guid>>()))
|
||||
.Returns(new HashSet<Guid>());
|
||||
|
||||
var dtoService = BuildDtoService(libraryManager);
|
||||
|
||||
var options = new DtoOptions(false) { Fields = [ItemFields.MediaSourceCount] };
|
||||
var dtos = dtoService.GetBaseItemDtos([movieOne, movieTwo], options);
|
||||
|
||||
Assert.Equal(2, dtos.Count);
|
||||
|
||||
// A single media source is the default, so the count is left unset (the client treats null as one).
|
||||
foreach (var dto in dtos)
|
||||
{
|
||||
Assert.Null(dto.MediaSourceCount);
|
||||
}
|
||||
|
||||
// The alternate-version check is batched once for the whole set, and the per-item lookups are
|
||||
// never reached because the batch already ruled out alternate versions.
|
||||
libraryManager.Verify(x => x.GetItemIdsWithAlternateVersions(It.IsAny<IReadOnlyList<Guid>>()), Times.Once);
|
||||
libraryManager.Verify(x => x.GetLinkedAlternateVersions(It.IsAny<Video>()), Times.Never);
|
||||
libraryManager.Verify(x => x.GetLocalAlternateVersionIds(It.IsAny<Video>()), Times.Never);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetBaseItemDtos_VideoInAlternateVersionBatch_ResolvesRealCount()
|
||||
{
|
||||
var movie = new Movie
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = "Movie",
|
||||
ImageInfos = []
|
||||
};
|
||||
|
||||
var libraryManager = new Mock<ILibraryManager>();
|
||||
|
||||
// This movie IS in the batch set, so the fast path must not short-circuit it: the per-item
|
||||
// lookups still run and the count is computed exactly as it was before batching. Two linked
|
||||
// alternate versions plus the movie itself is a count of three.
|
||||
libraryManager
|
||||
.Setup(x => x.GetItemIdsWithAlternateVersions(It.IsAny<IReadOnlyList<Guid>>()))
|
||||
.Returns(new HashSet<Guid> { movie.Id });
|
||||
libraryManager
|
||||
.Setup(x => x.GetLinkedAlternateVersions(It.IsAny<Video>()))
|
||||
.Returns([new Movie { Id = Guid.NewGuid() }, new Movie { Id = Guid.NewGuid() }]);
|
||||
libraryManager
|
||||
.Setup(x => x.GetLocalAlternateVersionIds(It.IsAny<Video>()))
|
||||
.Returns([]);
|
||||
|
||||
var dtoService = BuildDtoService(libraryManager);
|
||||
|
||||
var options = new DtoOptions(false) { Fields = [ItemFields.MediaSourceCount] };
|
||||
var dtos = dtoService.GetBaseItemDtos([movie], options);
|
||||
|
||||
Assert.Single(dtos);
|
||||
Assert.Equal(3, dtos[0].MediaSourceCount);
|
||||
libraryManager.Verify(x => x.GetItemIdsWithAlternateVersions(It.IsAny<IReadOnlyList<Guid>>()), Times.Once);
|
||||
}
|
||||
|
||||
private static DtoService BuildDtoService(BaseItem displayParent)
|
||||
{
|
||||
var libraryManager = new Mock<ILibraryManager>();
|
||||
@@ -231,6 +310,10 @@ public class DtoServiceImageInheritanceTests
|
||||
.Setup(x => x.GetImageCacheTag(It.IsAny<BaseItem>(), It.IsAny<ItemImageInfo>()))
|
||||
.Returns<BaseItem, ItemImageInfo>((_, image) => image.Path);
|
||||
|
||||
// Video.IsActiveRecording() dereferences this static during DTO building.
|
||||
Video.RecordingsManager = recordingsManager.Object;
|
||||
BaseItem.LibraryManager = libraryManager.Object;
|
||||
|
||||
return new DtoService(
|
||||
logger.Object,
|
||||
libraryManager.Object,
|
||||
|
||||
Reference in New Issue
Block a user