From 60b80c88b60b356abc0ab3e27ef9b09817b34b30 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Jan 2022 00:49:16 +0900 Subject: [PATCH 1/2] Avoid file retrieval overhead when detaching `BeatmapSetInfo` It seems that no usages of `BeatmapSetInfo` detaches require files - a `WorkingBeatmap` is always obtained before doing further lookups. Therefore we can omit this include unless the detaching object is a `BeatmapInfo`. A refetch is performed when retrieving a `WorkingBeatmap` to complete the equation. --- osu.Game/Beatmaps/BeatmapManager.cs | 18 +++++++++++++++- osu.Game/Database/RealmObjectExtensions.cs | 24 +++++++++++++++------- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index f901b0fbc1..d983c087a0 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -299,7 +299,23 @@ namespace osu.Game.Beatmaps #region Implementation of IWorkingBeatmapCache - public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo? importedBeatmap) => workingBeatmapCache.GetWorkingBeatmap(importedBeatmap); + public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo? importedBeatmap) + { + // Detached sets don't come with files. + // If we seem to be missing files, now is a good time to re-fetch. + if (importedBeatmap?.BeatmapSet?.Files.Count == 0) + { + using (var realm = contextFactory.CreateContext()) + { + var refetch = realm.Find(importedBeatmap.ID)?.Detach(); + + if (refetch != null) + importedBeatmap = refetch; + } + } + + return workingBeatmapCache.GetWorkingBeatmap(importedBeatmap); + } public WorkingBeatmap GetWorkingBeatmap(ILive? importedBeatmap) { diff --git a/osu.Game/Database/RealmObjectExtensions.cs b/osu.Game/Database/RealmObjectExtensions.cs index 5ab7154486..140f41c5d8 100644 --- a/osu.Game/Database/RealmObjectExtensions.cs +++ b/osu.Game/Database/RealmObjectExtensions.cs @@ -76,6 +76,14 @@ namespace osu.Game.Database { applyCommonConfiguration(c); + c.CreateMap() + .MaxDepth(2) + .AfterMap((s, d) => + { + foreach (var beatmap in d.Beatmaps) + beatmap.BeatmapSet = d; + }); + // This can be further optimised to reduce cyclic retrievals, similar to the optimised set mapper below. // Only hasn't been done yet as we detach at the point of BeatmapInfo less often. c.CreateMap() @@ -100,6 +108,15 @@ namespace osu.Game.Database { applyCommonConfiguration(c); + c.CreateMap() + .MaxDepth(2) + .ForMember(b => b.Files, cc => cc.Ignore()) + .AfterMap((s, d) => + { + foreach (var beatmap in d.Beatmaps) + beatmap.BeatmapSet = d; + }); + c.CreateMap() .MaxDepth(1) .ForMember(b => b.BeatmapSet, cc => cc.Ignore()); @@ -131,13 +148,6 @@ namespace osu.Game.Database c.CreateMap(); c.CreateMap(); c.CreateMap(); - c.CreateMap() - .MaxDepth(2) - .AfterMap((s, d) => - { - foreach (var beatmap in d.Beatmaps) - beatmap.BeatmapSet = d; - }); } /// From 1db2135d7022f16c58f4131c1ffaec6494002be0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Jan 2022 01:05:22 +0900 Subject: [PATCH 2/2] Update tests to match new behaviour --- osu.Game.Tests/Database/BeatmapImporterTests.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Database/BeatmapImporterTests.cs b/osu.Game.Tests/Database/BeatmapImporterTests.cs index 1bba73bea0..227314cffd 100644 --- a/osu.Game.Tests/Database/BeatmapImporterTests.cs +++ b/osu.Game.Tests/Database/BeatmapImporterTests.cs @@ -57,8 +57,9 @@ namespace osu.Game.Tests.Database { detachedBeatmapSet = live.Detach(); - Assert.AreEqual(live.Files.Count, detachedBeatmapSet.Files.Count); - Assert.AreEqual(live.Files.Select(f => f.File).Count(), detachedBeatmapSet.Files.Select(f => f.File).Count()); + // files are omitted + Assert.AreEqual(0, detachedBeatmapSet.Files.Count); + Assert.AreEqual(live.Beatmaps.Count, detachedBeatmapSet.Beatmaps.Count); Assert.AreEqual(live.Beatmaps.Select(f => f.Difficulty).Count(), detachedBeatmapSet.Beatmaps.Select(f => f.Difficulty).Count()); Assert.AreEqual(live.Metadata, detachedBeatmapSet.Metadata); @@ -67,11 +68,9 @@ namespace osu.Game.Tests.Database Debug.Assert(detachedBeatmapSet != null); // Check detached instances can all be accessed without throwing. - Assert.NotNull(detachedBeatmapSet.Files.Count); - Assert.NotZero(detachedBeatmapSet.Files.Select(f => f.File).Count()); + Assert.AreEqual(0, detachedBeatmapSet.Files.Count); Assert.NotNull(detachedBeatmapSet.Beatmaps.Count); Assert.NotZero(detachedBeatmapSet.Beatmaps.Select(f => f.Difficulty).Count()); - Assert.NotNull(detachedBeatmapSet.Beatmaps.First().Path); Assert.NotNull(detachedBeatmapSet.Metadata); // Check cyclic reference to beatmap set @@ -96,9 +95,12 @@ namespace osu.Game.Tests.Database Assert.NotNull(beatmapSet); Debug.Assert(beatmapSet != null); - BeatmapSetInfo? detachedBeatmapSet = null; + // Detach at the BeatmapInfo point, similar to what GetWorkingBeatmap does. + BeatmapInfo? detachedBeatmap = null; - beatmapSet.PerformRead(s => detachedBeatmapSet = s.Detach()); + beatmapSet.PerformRead(s => detachedBeatmap = s.Beatmaps.First().Detach()); + + BeatmapSetInfo? detachedBeatmapSet = detachedBeatmap?.BeatmapSet; Debug.Assert(detachedBeatmapSet != null);