mirror of
https://github.com/ppy/osu.git
synced 2024-12-15 06:43:21 +08:00
Rewrite TopLocalRank
to use realm subscriptions
This addresses the number one performance concern with realm (when entering song select). Previous logic was causing instantiation and property reads of every score in the database due to the `AsEnumerable` specfication.
This commit is contained in:
parent
d10d657073
commit
27ea37c690
@ -6,9 +6,9 @@ using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Models;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Leaderboards;
|
||||
using osu.Game.Rulesets;
|
||||
@ -30,72 +30,45 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
|
||||
private IDisposable scoreSubscription;
|
||||
|
||||
private bool rankUpdatePending;
|
||||
|
||||
public TopLocalRank(BeatmapInfo beatmapInfo)
|
||||
: base(null)
|
||||
{
|
||||
this.beatmapInfo = beatmapInfo;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
ruleset.ValueChanged += _ => fetchAndLoadTopScore();
|
||||
|
||||
fetchAndLoadTopScore();
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
scoreSubscription = realmFactory.Context.All<ScoreInfo>()
|
||||
.Filter($"{nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.ID)} = $0", beatmapInfo.ID)
|
||||
.QueryAsyncWithNotifications((_, changes, ___) =>
|
||||
{
|
||||
if (changes == null)
|
||||
return;
|
||||
|
||||
fetchTopScoreRank();
|
||||
});
|
||||
}
|
||||
|
||||
private IDisposable scoreSubscription;
|
||||
|
||||
private ScheduledDelegate scheduledRankUpdate;
|
||||
|
||||
private void fetchAndLoadTopScore()
|
||||
{
|
||||
// TODO: this lookup likely isn't required, we can use the results of the subscription directly.
|
||||
var rank = fetchTopScoreRank();
|
||||
|
||||
scheduledRankUpdate = Scheduler.Add(() =>
|
||||
ruleset.BindValueChanged(_ =>
|
||||
{
|
||||
Rank = rank;
|
||||
|
||||
rankUpdatePending = true;
|
||||
// Required since presence is changed via IsPresent override
|
||||
Invalidate(Invalidation.Presence);
|
||||
});
|
||||
|
||||
scoreSubscription?.Dispose();
|
||||
scoreSubscription = realmFactory.Context.All<ScoreInfo>()
|
||||
.Filter($"{nameof(ScoreInfo.User)}.{nameof(RealmUser.OnlineID)} == $0"
|
||||
+ $"&& {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.ID)} == $1"
|
||||
+ $"&& {nameof(ScoreInfo.Ruleset)}.{nameof(RulesetInfo.ShortName)} == $2"
|
||||
+ $"&& {nameof(ScoreInfo.DeletePending)} == false", api.LocalUser.Value.Id, beatmapInfo.ID, ruleset.Value.ShortName)
|
||||
.OrderByDescending(s => s.TotalScore)
|
||||
.QueryAsyncWithNotifications((items, changes, ___) =>
|
||||
{
|
||||
if (changes == null)
|
||||
rankUpdatePending = false;
|
||||
|
||||
Rank = items.FirstOrDefault()?.Rank;
|
||||
});
|
||||
}, true);
|
||||
}
|
||||
|
||||
// We're present if a rank is set, or if there is a pending rank update (IsPresent = true is required for the scheduler to run).
|
||||
public override bool IsPresent => base.IsPresent && (Rank != null || scheduledRankUpdate?.Completed == false);
|
||||
|
||||
private ScoreRank? fetchTopScoreRank()
|
||||
{
|
||||
if (realmFactory == null || beatmapInfo == null || ruleset?.Value == null || api?.LocalUser.Value == null)
|
||||
return null;
|
||||
|
||||
using (var realm = realmFactory.CreateContext())
|
||||
{
|
||||
return realm.All<ScoreInfo>()
|
||||
.AsEnumerable()
|
||||
// TODO: update to use a realm filter directly (or at least figure out the beatmap part to reduce scope).
|
||||
.Where(s => s.UserID == api.LocalUser.Value.Id && s.BeatmapInfoID == beatmapInfo.ID && s.RulesetID == ruleset.Value.ID && !s.DeletePending)
|
||||
.OrderByDescending(s => s.TotalScore)
|
||||
.FirstOrDefault()
|
||||
?.Rank;
|
||||
}
|
||||
}
|
||||
public override bool IsPresent => base.IsPresent && (Rank != null || rankUpdatePending);
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user