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> /// </summary>
public class StatisticsEndpoints : ApiEndpointController public class StatisticsEndpoints : ApiEndpointController
{ {
private readonly Database database;
public StatisticsEndpoints(Database database)
{
this.database = database;
}
/// <summary> /// <summary>
/// Gets everything that StatisticsHelper provides. /// Gets everything that StatisticsHelper provides.
/// </summary> /// </summary>
@ -21,11 +29,11 @@ public class StatisticsEndpoints : ApiEndpointController
( (
new StatisticsResponse new StatisticsResponse
{ {
Photos = await StatisticsHelper.PhotoCount(), Photos = await StatisticsHelper.PhotoCount(this.database),
Slots = await StatisticsHelper.SlotCount(), Slots = await StatisticsHelper.SlotCount(this.database),
Users = await StatisticsHelper.UserCount(), Users = await StatisticsHelper.UserCount(this.database),
RecentMatches = await StatisticsHelper.RecentMatches(), RecentMatches = await StatisticsHelper.RecentMatches(this.database),
TeamPicks = await StatisticsHelper.TeamPickCount(), 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 // This is hella ugly but it technically assigns the proper rank to a score
// var needed for Anonymous type returned from SELECT // var needed for Anonymous type returned from SELECT
var rankedScores = this.database.Scores var rankedScores = this.database.Scores
.Where(s => playerIds == null || playerIds.Contains(s.MainPlayer))
.Where(s => s.SlotId == slotId && s.Type == type) .Where(s => s.SlotId == slotId && s.Type == type)
.AsEnumerable()
.Where(s => playerIds == null || playerIds.Any(id => s.PlayerIdCollection.Contains(id)))
.OrderByDescending(s => s.Points) .OrderByDescending(s => s.Points)
.ThenBy(s => s.ScoreId) .ThenBy(s => s.ScoreId)
.ToList() .ToList()

View file

@ -182,7 +182,7 @@ public class SlotsController : ControllerBase
string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize(gameVersion)); string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize(gameVersion));
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots); 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)); return this.Ok(generateSlotsResponse(response, start, total));
} }
@ -238,9 +238,9 @@ public class SlotsController : ControllerBase
.Skip(Math.Max(0, pageStart - 1)) .Skip(Math.Max(0, pageStart - 1))
.Take(Math.Min(pageSize, 30)); .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 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)); return this.Ok(generateSlotsResponse(response, start, total));
} }
@ -290,7 +290,7 @@ public class SlotsController : ControllerBase
.Take(Math.Min(pageSize, 30)); .Take(Math.Min(pageSize, 30));
string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize(gameVersion)); string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize(gameVersion));
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots); 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)); 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)); string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(gameVersion));
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots); 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)); 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)); string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion));
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots); 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)); 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)); string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion));
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots); 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)); 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)); string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion));
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots); 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)); return this.Ok(generateSlotsResponse(response, start, total));
} }

View file

@ -9,15 +9,23 @@ namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
[Produces("text/plain")] [Produces("text/plain")]
public class StatisticsController : ControllerBase public class StatisticsController : ControllerBase
{ {
private readonly Database database;
public StatisticsController(Database database)
{
this.database = database;
}
[HttpGet("playersInPodCount")] [HttpGet("playersInPodCount")]
[HttpGet("totalPlayerCount")] [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")] [HttpGet("planetStats")]
public async Task<IActionResult> PlanetStats() public async Task<IActionResult> PlanetStats()
{ {
int totalSlotCount = await StatisticsHelper.SlotCount(); int totalSlotCount = await StatisticsHelper.SlotCount(this.database);
int mmPicksCount = await StatisticsHelper.TeamPickCount(); int mmPicksCount = await StatisticsHelper.TeamPickCount(this.database);
return this.Ok return this.Ok
( (
@ -27,5 +35,5 @@ public class StatisticsController : ControllerBase
} }
[HttpGet("planetStats/totalLevelCount")] [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 == null) return this.Redirect("~/login");
if (!user.IsAdmin) return this.NotFound(); if (!user.IsAdmin) return this.NotFound();
this.Statistics.Add(new AdminPanelStatistic("Users", await StatisticsHelper.UserCount(), "/admin/users")); this.Statistics.Add(new AdminPanelStatistic("Users", await StatisticsHelper.UserCount(this.Database), "/admin/users"));
this.Statistics.Add(new AdminPanelStatistic("Slots", await StatisticsHelper.SlotCount())); this.Statistics.Add(new AdminPanelStatistic("Slots", await StatisticsHelper.SlotCount(this.Database)));
this.Statistics.Add(new AdminPanelStatistic("Photos", await StatisticsHelper.PhotoCount())); this.Statistics.Add(new AdminPanelStatistic("Photos", await StatisticsHelper.PhotoCount(this.Database)));
this.Statistics.Add(new AdminPanelStatistic("API Keys", await StatisticsHelper.APIKeyCount(), "/admin/keys")); this.Statistics.Add(new AdminPanelStatistic("API Keys", await StatisticsHelper.APIKeyCount(this.Database), "/admin/keys"));
if (!string.IsNullOrEmpty(command)) if (!string.IsNullOrEmpty(command))
{ {

View file

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

View file

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

View file

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

View file

@ -1,35 +1,36 @@
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Administration; using LBPUnion.ProjectLighthouse.Administration;
using LBPUnion.ProjectLighthouse.Extensions;
using LBPUnion.ProjectLighthouse.Levels; using LBPUnion.ProjectLighthouse.Levels;
using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.PlayerData;
using LBPUnion.ProjectLighthouse.Types;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Helpers; namespace LBPUnion.ProjectLighthouse.Helpers;
public static class StatisticsHelper 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(); => 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 #region Moderator/Admin specific
public static async Task<int> ReportCount() => await database.Reports.CountAsync(); public static async Task<int> ReportCount(Database database) => await database.Reports.CountAsync();
public static async Task<int> CaseCount() => await database.Cases.CountAsync(); public static async Task<int> CaseCount(Database database) => await database.Cases.CountAsync();
public static async Task<int> DismissedCaseCount() => await database.Cases.CountAsync(c => c.DismissedAt != null); public static async Task<int> DismissedCaseCount(Database database) => await database.Cases.CountAsync(c => c.DismissedAt != null);
#endregion #endregion
public static async Task<int> APIKeyCount() => await database.APIKeys.CountAsync(); public static async Task<int> APIKeyCount(Database database) => await database.APIKeys.CountAsync();
} }