From 0f396e99f712a33254caf5715fdbcc0314c93db1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Jan 2025 18:53:56 +0900 Subject: [PATCH] Fix stale top score position being returned resulting in incorret profile / medal awarding Addresses https://github.com/ppy/osu-web/issues/11765. --- .../Controllers/BeatmapItem.cs | 46 +++++++++++++------ 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/GlobalRankLookupCache/Controllers/BeatmapItem.cs b/GlobalRankLookupCache/Controllers/BeatmapItem.cs index cfa66e4..4e92f62 100644 --- a/GlobalRankLookupCache/Controllers/BeatmapItem.cs +++ b/GlobalRankLookupCache/Controllers/BeatmapItem.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using MySqlConnector; namespace GlobalRankLookupCache.Controllers { @@ -17,7 +18,7 @@ internal class BeatmapItem private bool isQualified; - private readonly TaskCompletionSource populated = new TaskCompletionSource(); + private TaskCompletionSource populated = new TaskCompletionSource(); public BeatmapItem(int beatmapId, string highScoresTable) { @@ -33,7 +34,7 @@ public BeatmapItem(int beatmapId, string highScoresTable) await Task.WhenAny(Task.Delay(1000), Task.Run(repopulateScores)); // may change due to re-population; use a local copy - var scores = Scores; + List scores = Scores; if (!populated.Task.IsCompleted) { @@ -41,28 +42,27 @@ public BeatmapItem(int beatmapId, string highScoresTable) using (var db = await Program.GetDatabaseConnection()) using (var cmd = db.CreateCommand()) { - cmd.CommandTimeout = 10; - Interlocked.Increment(ref RankLookupController.Misses); + cmd.CommandTimeout = 10; cmd.CommandText = $"select count(*) from {highScoresTable} where beatmap_id = {beatmapId} and hidden = 0"; - int total = (int)(long)(await cmd.ExecuteScalarAsync())!; - if (total < 2000) + int roughTotal = (int)(long)(await cmd.ExecuteScalarAsync())!; + + if (roughTotal < 2000) { cmd.CommandText = $"select count(DISTINCT user_id) from {highScoresTable} where beatmap_id = {beatmapId} and hidden = 0"; - total = (int)(long)(await cmd.ExecuteScalarAsync())!; + int accurateTotal = (int)(long)(await cmd.ExecuteScalarAsync())!; - cmd.CommandText = $"select count(DISTINCT user_id) from {highScoresTable} where beatmap_id = {beatmapId} and score > {score} and hidden = 0"; - int pos = (int)(long)(await cmd.ExecuteScalarAsync())!; - return (pos, total, true); + int accuratePosition = await getAccuratePosition(db, score); + return (accuratePosition, accurateTotal, true); } else { cmd.CommandText = $"select count(*) from {highScoresTable} where beatmap_id = {beatmapId} and score > {score} and hidden = 0"; - int pos = (int)(long)(await cmd.ExecuteScalarAsync())!; + int roughPosition = (int)(long)(await cmd.ExecuteScalarAsync())!; - return (pos, total, false); + return (roughPosition, roughTotal, false); } } } @@ -77,7 +77,27 @@ public BeatmapItem(int beatmapId, string highScoresTable) Interlocked.Increment(ref RankLookupController.Hits); int result = scores.BinarySearch(score + 1); - return (scores.Count - (result < 0 ? ~result : result), scores.Count, true); + int position = (scores.Count - (result < 0 ? ~result : result)); + + // A new top score was achieved. + // To ensure medals and profiles are updated accurately, require a re-fetch at this point. + if (position < 500) + { + using (var db = await Program.GetDatabaseConnection()) + position = await getAccuratePosition(db, score); + } + + return (position, scores.Count, true); + } + + private async Task getAccuratePosition(MySqlConnection db, int score) + { + using (var cmd = db.CreateCommand()) + { + cmd.CommandTimeout = 10; + cmd.CommandText = $"select count(DISTINCT user_id) from {highScoresTable} where beatmap_id = {beatmapId} and score > {score} and hidden = 0"; + return (int)(long)(await cmd.ExecuteScalarAsync())!; + } } private readonly SemaphoreSlim populationSemaphore = new SemaphoreSlim(1);