diff --git a/GenOnlineService/ExternalLeaderboardsClient.cs b/GenOnlineService/ExternalLeaderboardsClient.cs index 4f14452..6fc6e12 100644 --- a/GenOnlineService/ExternalLeaderboardsClient.cs +++ b/GenOnlineService/ExternalLeaderboardsClient.cs @@ -10,6 +10,7 @@ using System.Text; using System.Text.Json; using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Polly; @@ -122,7 +123,7 @@ private static SocketsHttpHandler CreateLeaderboardsHandler() }; } - public static async Task PostMatchResultAsync(AppDbContext db, Lobby lobby) + public static async Task PostMatchResultAsync(IDbContextFactory dbFactory, Lobby lobby) { if (lobby.MatchID == 0) return; @@ -131,8 +132,13 @@ public static async Task PostMatchResultAsync(AppDbContext db, Lobby lobby) { GetExternalLeaderboardsConfig(out string postUrl, out _, out string postToken, out _); - // Load the match payload - var matchEntry = await Database.MatchHistory.LoadMatchHistoryEntryAsync(db, (long)lobby.MatchID); + // Dispose the read context before the potentially multi-minute external request below. + Controllers.MatchHistory_Entry? matchEntry; + await using (var readDb = await dbFactory.CreateDbContextAsync()) + { + matchEntry = await Database.MatchHistory.LoadMatchHistoryEntryAsync(readDb, (long)lobby.MatchID); + } + if (matchEntry == null) { Console.WriteLine($"[WARNING] MatchHistory entry not found for match ID {lobby.MatchID}"); @@ -206,6 +212,9 @@ await retryPolicy.ExecuteAsync(async () => // Only player IDs that were actually part of this match are valid recipients of an ELO update. var expectedPlayerIds = new HashSet(matchEntry.members.Where(m => m.HasValue).Select(m => m.Value.user_id)); + // Fresh, short-lived context just for the persistence writes below. + await using var writeDb = await dbFactory.CreateDbContextAsync(); + foreach (var (userId, updatedPlayer) in refreshResponse.data) { if (!expectedPlayerIds.Contains(userId)) @@ -228,7 +237,7 @@ await retryPolicy.ExecuteAsync(async () => } // Call SaveELOData to persist as fallback - await Database.Users.SaveELOData(db, userId, new EloData(newRating, newMonthlyRating, newMatches)); + await Database.Users.SaveELOData(writeDb, userId, new EloData(newRating, newMonthlyRating, newMatches)); } } catch (Exception ex) diff --git a/GenOnlineService/LobbyManager.cs b/GenOnlineService/LobbyManager.cs index 4937c3a..239de9b 100644 --- a/GenOnlineService/LobbyManager.cs +++ b/GenOnlineService/LobbyManager.cs @@ -1406,15 +1406,10 @@ public class LobbyManager private Int64 m_NextLobbyID = 0; private readonly IServiceProvider _services; - private readonly AppDbContext _db; public LobbyManager(IServiceProvider services) { _services = services; - - var scope = _services.CreateScope(); - var factory = scope.ServiceProvider.GetRequiredService>(); - var _db = factory.CreateDbContext(); } public async Task Cleanup() @@ -1720,13 +1715,17 @@ public async Task DeleteLobby(Lobby lobby) { try { + using var scope = _services.CreateScope(); + var factory = scope.ServiceProvider.GetRequiredService>(); + await using var db = await factory.CreateDbContextAsync(); + if (lobby.State != ELobbyState.COMPLETE) { // make done await lobby.UpdateState(ELobbyState.COMPLETE); // attempt to commit it - await Database.MatchHistory.CommitLobbyToMatchHistory(_db, lobby); + await Database.MatchHistory.CommitLobbyToMatchHistory(db, lobby); } // delete @@ -1740,11 +1739,11 @@ public async Task DeleteLobby(Lobby lobby) lobby.OnLobbyNeedsDestroyed -= HandleLobbyNeedsDestroyed; // make sure we have a winner - await Database.MatchHistory.DetermineLobbyWinnerIfNotPresent(_db, lobby); + await Database.MatchHistory.DetermineLobbyWinnerIfNotPresent(db, lobby); // Post match result to external leaderboard API for every lobby type. // Only QuickMatch responses are expected to carry a ratings body. - await ExternalLeaderboardsClient.PostMatchResultAsync(_db, lobby); + await ExternalLeaderboardsClient.PostMatchResultAsync(factory, lobby); } return bRemoved;