Skip to content

Commit

Permalink
Add pregeneration to watched profiles to speed up load times
Browse files Browse the repository at this point in the history
  • Loading branch information
Simyon264 committed Jun 24, 2024
1 parent ea27dbd commit e75ef08
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 15 deletions.
36 changes: 21 additions & 15 deletions ReplayBrowser/Helpers/ReplayHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,25 +60,28 @@ public async Task<List<Replay>> GetMostRecentReplays(AuthenticationState state)
/// Fetches a player profile from the database.
/// </summary>
/// <exception cref="UnauthorizedAccessException">Thrown when the account is private and the requestor is not the account owner or an admin.</exception>
public async Task<CollectedPlayerData?> GetPlayerProfile(Guid playerGuid, AuthenticationState authenticationState)
public async Task<CollectedPlayerData?> GetPlayerProfile(Guid playerGuid, AuthenticationState authenticationState, bool skipCache = false, bool skipPermsCheck = false)
{
var accountCaller = await _accountService.GetAccount(authenticationState);

var accountRequested = _accountService.GetAccountSettings(playerGuid);
if (accountRequested is { RedactInformation: true })

if (!skipPermsCheck)
{
if (accountCaller == null || !accountCaller.IsAdmin)
if (accountRequested is { RedactInformation: true })
{
if (accountCaller?.Guid != playerGuid)
if (accountCaller == null || !accountCaller.IsAdmin)
{
throw new UnauthorizedAccessException("The account you are trying to view is private. Contact the account owner and ask them to make their account public.");
if (accountCaller?.Guid != playerGuid)
{
throw new UnauthorizedAccessException("The account you are trying to view is private. Contact the account owner and ask them to make their account public.");
}
}
}
}

var cacheKey = $"player-{playerGuid}";
if (_cache.TryGetValue(cacheKey, out CollectedPlayerData cachedPlayerData))
if (_cache.TryGetValue(cacheKey, out CollectedPlayerData cachedPlayerData) && !skipCache)
{
return cachedPlayerData;
}
Expand Down Expand Up @@ -218,15 +221,18 @@ public async Task<List<Replay>> GetMostRecentReplays(AuthenticationState state)
{
collectedPlayerData.IsWatched = accountCaller.SavedProfiles.Contains(playerGuid);
}
await _accountService.AddHistory(accountCaller, new HistoryEntry()

if (!skipPermsCheck)
{
Action = Enum.GetName(typeof(Action), Action.ProfileViewed) ?? "Unknown",
Time = DateTime.UtcNow,
Details = $"Player GUID: {playerGuid} Username: {collectedPlayerData.PlayerData.Username}"
});
await _accountService.AddHistory(accountCaller, new HistoryEntry()
{
Action = Enum.GetName(typeof(Action), Action.ProfileViewed) ?? "Unknown",
Time = DateTime.UtcNow,
Details = $"Player GUID: {playerGuid} Username: {collectedPlayerData.PlayerData.Username}"
});
}

_cache.Set(cacheKey, collectedPlayerData, TimeSpan.FromMinutes(20));
_cache.Set(cacheKey, collectedPlayerData, TimeSpan.FromMinutes(60));

return collectedPlayerData;
}
Expand Down Expand Up @@ -513,7 +519,7 @@ public async Task<SearchResult> SearchReplays(List<SearchQueryItem> searchItems,
paginatedResults.Add((paginatedList, totalItems));
}

_cache.Set(cacheKey, paginatedResults, TimeSpan.FromMinutes(5));
_cache.Set(cacheKey, paginatedResults, TimeSpan.FromMinutes(35));

stopWatch.Stop();
Log.Information("Search took " + stopWatch.ElapsedMilliseconds + "ms.");
Expand Down
72 changes: 72 additions & 0 deletions ReplayBrowser/Services/ProfilePregeneratorService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System.Diagnostics;
using System.Security.Claims;
using Microsoft.AspNetCore.Components.Authorization;
using ReplayBrowser.Data;
using ReplayBrowser.Helpers;
using Serilog;

namespace ReplayBrowser.Services;

/// <summary>
/// Service that checks every watched profile for every user and pregenerates the profile if it is not already generated for better loading times.
/// </summary>
public class ProfilePregeneratorService : IHostedService, IDisposable, IAsyncDisposable
{
private Timer? _timer = null;
private readonly IServiceScopeFactory _scopeFactory;

public ProfilePregeneratorService(IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
}

public Task StartAsync(CancellationToken cancellationToken)
{
_timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromMinutes(30));
return Task.CompletedTask;
}

public Task StopAsync(CancellationToken cancellationToken)
{
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}

private async void DoWork(object? state)
{
var sw = new Stopwatch();
Log.Information("Starting profile pregeneration.");
sw.Start();

using var scope = _scopeFactory.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<ReplayDbContext>();
var replayHelper = scope.ServiceProvider.GetRequiredService<ReplayHelper>();
var profilesToGenerate = new List<Guid>();

dbContext.Accounts.Select(a => a.SavedProfiles).ToList().ForEach(profiles =>
{
foreach (var profile in profiles.Where(profile => !profilesToGenerate.Contains(profile)))
{
profilesToGenerate.Add(profile);
}
});

foreach (var guid in profilesToGenerate)
{
await replayHelper.GetPlayerProfile(guid, new AuthenticationState(new ClaimsPrincipal()), true, true);
Log.Information("Pregenerated profile for {Guid}.", guid);
}
sw.Stop();
Log.Information("Profile pregeneration finished in {ElapsedMilliseconds}ms.", sw.ElapsedMilliseconds);
}

public void Dispose()
{
_timer?.Dispose();
}

public async ValueTask DisposeAsync()
{
if (_timer != null) await _timer.DisposeAsync();
}
}
2 changes: 2 additions & 0 deletions ReplayBrowser/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,13 @@ public void ConfigureServices(IServiceCollection services)
services.AddSingleton<ReplayParserService>();
services.AddSingleton<AnalyticsService>();
services.AddSingleton<NoticeHelper>();
services.AddSingleton<ProfilePregeneratorService>();

services.AddHostedService<BackgroundServiceStarter<ReplayParserService>>();
services.AddHostedService<BackgroundServiceStarter<AccountService>>();
services.AddHostedService<BackgroundServiceStarter<LeaderboardService>>();
services.AddHostedService<BackgroundServiceStarter<AnalyticsService>>();
services.AddHostedService<BackgroundServiceStarter<ProfilePregeneratorService>>();

services.AddScoped<ReplayHelper>();

Expand Down

0 comments on commit e75ef08

Please sign in to comment.