From 84cce2f0d13840b8dc4e4ed03893e8c7ece42973 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Apr 2026 00:02:32 +0900 Subject: [PATCH] Improve efficiency of tag population (#37228) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - No longer holds realm write transaction open while performing sqlite lookups. - No longer attempts a write transaction when it will be a noop. I'll admit that this is maybe working around the actual realm write part being slow, but as I can't profile the issue locally, the sluggishness may actually be in sqlite for those users affected (since it's only been reported for tag population and not difficulty calculation?). Regardless, this should fix the issue this iteration. I also adjusted the user messaging to let them know why tag population is happening, since we've had some questions as to why it's running in the first place (it only happens once a month, so that's understandable). - [x] Depends on https://github.com/ppy/osu/pull/37227. - Closes https://github.com/ppy/osu/issues/34699. - Closes https://github.com/ppy/osu/issues/37210. Note that https://github.com/ppy/osu/pull/36128 also exists and has valid improvements which can be addressed separately. This is intended to be something we can act on immediately. --------- Co-authored-by: Bartłomiej Dach --- .../Database/BackgroundDataStoreProcessor.cs | 58 ++++++++++++------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/osu.Game/Database/BackgroundDataStoreProcessor.cs b/osu.Game/Database/BackgroundDataStoreProcessor.cs index f8d1b9ae51..1b64990fde 100644 --- a/osu.Game/Database/BackgroundDataStoreProcessor.cs +++ b/osu.Game/Database/BackgroundDataStoreProcessor.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -655,7 +656,8 @@ namespace osu.Game.Database if (metadataSourceFetchDate <= lastPopulation) { - Logger.Log($@"Skipping user tag population because the local metadata source hasn't been updated since the last time user tags were checked ({lastPopulation.Value:d})"); + Logger.Log( + $@"Skipping user tag population because the local metadata source hasn't been updated since the last time user tags were checked ({lastPopulation.Value.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)})"); return; } @@ -675,9 +677,11 @@ namespace osu.Game.Database Logger.Log($@"Found {beatmapIds.Count} beatmaps with missing user tags."); - var notification = showProgressNotification(beatmapIds.Count, @"Populating missing user tags", @"beatmaps have had their tags updated."); + var notification = showProgressNotification(beatmapIds.Count, @"Populating missing user tags", + @"beatmaps have had their tags updated. This runs once a month to allow searching user tags."); int processedCount = 0; + int updatedCount = 0; int failedCount = 0; foreach (var id in beatmapIds) @@ -691,33 +695,37 @@ namespace osu.Game.Database try { - // Can't use async overload because we're not on the update thread. - // ReSharper disable once MethodHasAsyncOverload - realmAccess.Write(r => + var beatmap = realmAccess.Run(r => r.Find(id)?.Detach()); + + if (beatmap == null) continue; + + bool lookupSucceeded = localMetadataSource.TryLookup(beatmap, out var result); + + if (lookupSucceeded) { - BeatmapInfo beatmap = r.Find(id)!; + Debug.Assert(result != null); - bool lookupSucceeded = localMetadataSource.TryLookup(beatmap, out var result); + HashSet userTags = result.UserTags.ToHashSet(); - if (lookupSucceeded) + if (!userTags.SetEquals(beatmap.Metadata.UserTags)) { - Debug.Assert(result != null); - - var userTags = result.UserTags.ToHashSet(); - - if (!userTags.SetEquals(beatmap.Metadata.UserTags)) + ++updatedCount; + realmAccess.Write(r => { + beatmap = r.Find(id); + + if (beatmap == null) + return; + beatmap.Metadata.UserTags.Clear(); beatmap.Metadata.UserTags.AddRange(userTags); - return true; - } - - return false; + }); } - + } + else + { Logger.Log(@$"Could not find {beatmap.GetDisplayString()} in local cache while backpopulating missing user tags"); - return false; - }); + } ++processedCount; } @@ -732,7 +740,9 @@ namespace osu.Game.Database } } - completeNotification(notification, processedCount, beatmapIds.Count, failedCount); + // Report the updated item count rather than the total processed. Users don't really care about noops here. + completeNotification(notification, updatedCount, updatedCount, failedCount); + config.SetValue(OsuSetting.LastOnlineTagsPopulation, metadataSourceFetchDate); } @@ -753,7 +763,11 @@ namespace osu.Game.Database if (notification == null) return; - if (processedCount == totalCount) + if (totalCount == 0) + { + notification.CompleteSilently(); + } + else if (processedCount == totalCount) { notification.CompletionText = $"{processedCount} {notification.CompletionText}"; notification.Progress = 1;