Fix StatisticsHelper concurrent exception

This commit is contained in:
Slendy 2022-09-05 05:04:06 -05:00
parent cf5369d372
commit eb7cda8997
No known key found for this signature in database
GPG key ID: 7288D68361B91428
9 changed files with 65 additions and 44 deletions

View file

@ -10,6 +10,14 @@ namespace LBPUnion.ProjectLighthouse.Servers.API.Controllers;
/// </summary>
public class StatisticsEndpoints : ApiEndpointController
{
private readonly Database database;
public StatisticsEndpoints(Database database)
{
this.database = database;
}
/// <summary>
/// Gets everything that StatisticsHelper provides.
/// </summary>
@ -21,11 +29,11 @@ public class StatisticsEndpoints : ApiEndpointController
(
new StatisticsResponse
{
Photos = await StatisticsHelper.PhotoCount(),
Slots = await StatisticsHelper.SlotCount(),
Users = await StatisticsHelper.UserCount(),
RecentMatches = await StatisticsHelper.RecentMatches(),
TeamPicks = await StatisticsHelper.TeamPickCount(),
Photos = await StatisticsHelper.PhotoCount(this.database),
Slots = await StatisticsHelper.SlotCount(this.database),
Users = await StatisticsHelper.UserCount(this.database),
RecentMatches = await StatisticsHelper.RecentMatches(this.database),
TeamPicks = await StatisticsHelper.TeamPickCount(this.database),
}
);
}

View file

@ -206,8 +206,9 @@ public class ScoreController : ControllerBase
// This is hella ugly but it technically assigns the proper rank to a score
// var needed for Anonymous type returned from SELECT
var rankedScores = this.database.Scores
.Where(s => playerIds == null || playerIds.Contains(s.MainPlayer))
.Where(s => s.SlotId == slotId && s.Type == type)
.AsEnumerable()
.Where(s => playerIds == null || playerIds.Any(id => s.PlayerIdCollection.Contains(id)))
.OrderByDescending(s => s.Points)
.ThenBy(s => s.ScoreId)
.ToList()

View file

@ -182,7 +182,7 @@ public class SlotsController : ControllerBase
string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize(gameVersion));
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
int total = await StatisticsHelper.SlotCount();
int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion);
return this.Ok(generateSlotsResponse(response, start, total));
}
@ -238,9 +238,9 @@ public class SlotsController : ControllerBase
.Skip(Math.Max(0, pageStart - 1))
.Take(Math.Min(pageSize, 30));
string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize(gameVersion));
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(gameVersion));
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
int total = await StatisticsHelper.SlotCount();
int total = await StatisticsHelper.SlotCount(this.database);
return this.Ok(generateSlotsResponse(response, start, total));
}
@ -290,7 +290,7 @@ public class SlotsController : ControllerBase
.Take(Math.Min(pageSize, 30));
string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize(gameVersion));
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
int total = await StatisticsHelper.TeamPickCount();
int total = await StatisticsHelper.TeamPickCount(this.database);
return this.Ok(generateSlotsResponse(response, start, total));
}
@ -309,7 +309,7 @@ public class SlotsController : ControllerBase
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(gameVersion));
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
int total = await StatisticsHelper.SlotCount();
int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion);
return this.Ok(generateSlotsResponse(response, start, total));
}
@ -341,7 +341,7 @@ public class SlotsController : ControllerBase
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion));
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
int total = await StatisticsHelper.SlotCount();
int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion);
return this.Ok(generateSlotsResponse(response, start, total));
}
@ -387,7 +387,7 @@ public class SlotsController : ControllerBase
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion));
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
int total = await StatisticsHelper.SlotCount();
int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion);
return this.Ok(generateSlotsResponse(response, start, total));
}
@ -419,7 +419,7 @@ public class SlotsController : ControllerBase
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion));
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
int total = await StatisticsHelper.SlotCount();
int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion);
return this.Ok(generateSlotsResponse(response, start, total));
}

View file

@ -9,15 +9,23 @@ namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
[Produces("text/plain")]
public class StatisticsController : ControllerBase
{
private readonly Database database;
public StatisticsController(Database database)
{
this.database = database;
}
[HttpGet("playersInPodCount")]
[HttpGet("totalPlayerCount")]
public async Task<IActionResult> TotalPlayerCount() => this.Ok((await StatisticsHelper.RecentMatches()).ToString()!);
public async Task<IActionResult> TotalPlayerCount() => this.Ok((await StatisticsHelper.RecentMatches(this.database)).ToString());
[HttpGet("planetStats")]
public async Task<IActionResult> PlanetStats()
{
int totalSlotCount = await StatisticsHelper.SlotCount();
int mmPicksCount = await StatisticsHelper.TeamPickCount();
int totalSlotCount = await StatisticsHelper.SlotCount(this.database);
int mmPicksCount = await StatisticsHelper.TeamPickCount(this.database);
return this.Ok
(
@ -27,5 +35,5 @@ public class StatisticsController : ControllerBase
}
[HttpGet("planetStats/totalLevelCount")]
public async Task<IActionResult> TotalLevelCount() => this.Ok((await StatisticsHelper.SlotCount()).ToString());
public async Task<IActionResult> TotalLevelCount() => this.Ok((await StatisticsHelper.SlotCount(this.database)).ToString());
}

View file

@ -27,10 +27,10 @@ public class AdminPanelPage : BaseLayout
if (user == null) return this.Redirect("~/login");
if (!user.IsAdmin) return this.NotFound();
this.Statistics.Add(new AdminPanelStatistic("Users", await StatisticsHelper.UserCount(), "/admin/users"));
this.Statistics.Add(new AdminPanelStatistic("Slots", await StatisticsHelper.SlotCount()));
this.Statistics.Add(new AdminPanelStatistic("Photos", await StatisticsHelper.PhotoCount()));
this.Statistics.Add(new AdminPanelStatistic("API Keys", await StatisticsHelper.APIKeyCount(), "/admin/keys"));
this.Statistics.Add(new AdminPanelStatistic("Users", await StatisticsHelper.UserCount(this.Database), "/admin/users"));
this.Statistics.Add(new AdminPanelStatistic("Slots", await StatisticsHelper.SlotCount(this.Database)));
this.Statistics.Add(new AdminPanelStatistic("Photos", await StatisticsHelper.PhotoCount(this.Database)));
this.Statistics.Add(new AdminPanelStatistic("API Keys", await StatisticsHelper.APIKeyCount(this.Database), "/admin/keys"));
if (!string.IsNullOrEmpty(command))
{

View file

@ -21,15 +21,15 @@ public class ModPanelPage : BaseLayout
this.Statistics.Add(new AdminPanelStatistic(
statisticNamePlural: "Reports",
count: await StatisticsHelper.ReportCount(),
count: await StatisticsHelper.ReportCount(this.Database),
viewAllEndpoint: "/moderation/reports/0")
);
this.Statistics.Add(new AdminPanelStatistic(
statisticNamePlural: "Cases",
count: await StatisticsHelper.DismissedCaseCount(),
count: await StatisticsHelper.DismissedCaseCount(this.Database),
viewAllEndpoint: "/moderation/cases/0",
secondStatistic: await StatisticsHelper.CaseCount())
secondStatistic: await StatisticsHelper.CaseCount(this.Database))
);
return this.Page();

View file

@ -3,6 +3,7 @@ using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.PlayerData;
using LBPUnion.ProjectLighthouse.Servers.GameServer.Startup;
@ -47,14 +48,16 @@ public class MatchTests : LighthouseServerTest<GameServerTestStartup>
await semaphore.WaitAsync();
int oldPlayerCount = await StatisticsHelper.RecentMatches();
await using Database database = new();
int oldPlayerCount = await StatisticsHelper.RecentMatches(database);
HttpResponseMessage result = await this.AuthenticatedUploadDataRequest
("LITTLEBIGPLANETPS3_XML/match", Encoding.ASCII.GetBytes("[UpdateMyPlayerData,[\"Player\":\"1984\"]]"), loginResult.AuthTicket);
Assert.True(result.IsSuccessStatusCode);
int playerCount = await StatisticsHelper.RecentMatches();
int playerCount = await StatisticsHelper.RecentMatches(database);
semaphore.Release();
Assert.Equal(oldPlayerCount + 1, playerCount);

View file

@ -9,7 +9,6 @@ using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Extensions;
using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.PlayerData;
using LBPUnion.ProjectLighthouse.Types;
namespace LBPUnion.ProjectLighthouse.Helpers;
@ -27,20 +26,21 @@ public static class InfluxHelper
GameVersion.LittleBigPlanetPSP,
};
public static async void Log()
private static async void Log()
{
try
{
await using Database database = new();
using WriteApi writeApi = Client.GetWriteApi();
PointData point = PointData.Measurement("lighthouse")
.Field("playerCount", await StatisticsHelper.RecentMatches())
.Field("slotCount", await StatisticsHelper.SlotCount());
.Field("playerCount", await StatisticsHelper.RecentMatches(database))
.Field("slotCount", await StatisticsHelper.SlotCount(database));
foreach (GameVersion gameVersion in gameVersions)
{
PointData gamePoint = PointData.Measurement("lighthouse")
.Tag("game", gameVersion.ToString())
.Field("playerCountGame", await StatisticsHelper.RecentMatchesForGame(gameVersion));
.Field("playerCountGame", await StatisticsHelper.RecentMatchesForGame(database, gameVersion));
writeApi.WritePoint(gamePoint, ServerConfiguration.Instance.InfluxDB.Bucket, ServerConfiguration.Instance.InfluxDB.Organization);
}

View file

@ -1,35 +1,36 @@
using System.Linq;
using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Administration;
using LBPUnion.ProjectLighthouse.Extensions;
using LBPUnion.ProjectLighthouse.Levels;
using LBPUnion.ProjectLighthouse.PlayerData;
using LBPUnion.ProjectLighthouse.Types;
using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Helpers;
public static class StatisticsHelper
{
private static readonly Database database = new();
public static async Task<int> RecentMatches() => await database.LastContacts.Where(l => TimeHelper.Timestamp - l.Timestamp < 300).CountAsync();
public static async Task<int> RecentMatches(Database database) => await database.LastContacts.Where(l => TimeHelper.Timestamp - l.Timestamp < 300).CountAsync();
public static async Task<int> RecentMatchesForGame(GameVersion gameVersion)
public static async Task<int> RecentMatchesForGame(Database database, GameVersion gameVersion)
=> await database.LastContacts.Where(l => TimeHelper.Timestamp - l.Timestamp < 300 && l.GameVersion == gameVersion).CountAsync();
public static async Task<int> SlotCount() => await database.Slots.Where(s => s.Type == SlotType.User).CountAsync();
public static async Task<int> SlotCount(Database database) => await database.Slots.Where(s => s.Type == SlotType.User).CountAsync();
public static async Task<int> UserCount() => await database.Users.CountAsync(u => u.PermissionLevel != PermissionLevel.Banned);
public static async Task<int> SlotCountForGame(Database database, GameVersion gameVersion, bool includeSublevels = false) => await database.Slots.ByGameVersion(gameVersion, includeSublevels).CountAsync();
public static async Task<int> TeamPickCount() => await database.Slots.CountAsync(s => s.TeamPick);
public static async Task<int> UserCount(Database database) => await database.Users.CountAsync(u => u.PermissionLevel != PermissionLevel.Banned);
public static async Task<int> PhotoCount() => await database.Photos.CountAsync();
public static async Task<int> TeamPickCount(Database database) => await database.Slots.CountAsync(s => s.TeamPick);
public static async Task<int> PhotoCount(Database database) => await database.Photos.CountAsync();
#region Moderator/Admin specific
public static async Task<int> ReportCount() => await database.Reports.CountAsync();
public static async Task<int> CaseCount() => await database.Cases.CountAsync();
public static async Task<int> DismissedCaseCount() => await database.Cases.CountAsync(c => c.DismissedAt != null);
public static async Task<int> ReportCount(Database database) => await database.Reports.CountAsync();
public static async Task<int> CaseCount(Database database) => await database.Cases.CountAsync();
public static async Task<int> DismissedCaseCount(Database database) => await database.Cases.CountAsync(c => c.DismissedAt != null);
#endregion
public static async Task<int> APIKeyCount() => await database.APIKeys.CountAsync();
public static async Task<int> APIKeyCount(Database database) => await database.APIKeys.CountAsync();
}