From 1be3cc56683dbc7b9cc238d1793463b5a6550d2c Mon Sep 17 00:00:00 2001 From: SabreDartStudios <108193207+SabreDartStudios@users.noreply.github.com> Date: Tue, 4 Feb 2025 23:03:30 -0500 Subject: [PATCH] Some of the repository methods were not using Using and so it is possible connections are leaking. One customer found connections leaking in a playtest. Also, switched repositories from Transient/Scoped to Singleton. This will limit what can be done inside the repositories. --- src/OWSCharacterPersistence/Startup.cs | 12 +- .../MSSQL/CharactersRepository.cs | 2 +- .../MySQL/CharactersRepository.cs | 2 +- .../Postgres/CharactersRepository.cs | 186 ++++++++++-------- .../Postgres/InstanceManagementRepository.cs | 57 +++--- .../Postgres/UsersRepository.cs | 62 +++--- src/OWSData/SQL/GenericQueries.cs | 6 - src/OWSGlobalData/Startup.cs | 6 +- src/OWSInstanceManagement/Startup.cs | 18 +- src/OWSPublicAPI/Startup.cs | 12 +- 10 files changed, 195 insertions(+), 168 deletions(-) diff --git a/src/OWSCharacterPersistence/Startup.cs b/src/OWSCharacterPersistence/Startup.cs index b87f589cf..e78c0906a 100644 --- a/src/OWSCharacterPersistence/Startup.cs +++ b/src/OWSCharacterPersistence/Startup.cs @@ -138,16 +138,16 @@ private void InitializeContainer(IServiceCollection services) switch (dbBackend) { case "postgres": - container.Register(Lifestyle.Scoped); - container.Register(Lifestyle.Scoped); + container.Register(Lifestyle.Singleton); + container.Register(Lifestyle.Singleton); break; case "mysql": - container.Register(Lifestyle.Scoped); - container.Register(Lifestyle.Scoped); + container.Register(Lifestyle.Singleton); + container.Register(Lifestyle.Singleton); break; default: // Default to MSSQL - container.Register(Lifestyle.Scoped); - container.Register(Lifestyle.Scoped); + container.Register(Lifestyle.Singleton); + container.Register(Lifestyle.Singleton); break; } } diff --git a/src/OWSData/Repositories/Implementations/MSSQL/CharactersRepository.cs b/src/OWSData/Repositories/Implementations/MSSQL/CharactersRepository.cs index 17bc90900..58f7848d5 100644 --- a/src/OWSData/Repositories/Implementations/MSSQL/CharactersRepository.cs +++ b/src/OWSData/Repositories/Implementations/MSSQL/CharactersRepository.cs @@ -204,7 +204,7 @@ public async Task> GetDefaultCustomCharacterData( parameters.Add("@CustomerGUID", customerGUID); parameters.Add("@DefaultSetName", defaultSetName); - outputDefaultCustomCharacterDataRows = await Connection.QueryAsync(GenericQueries.GetDefaultCharacterCustomDataByName, + outputDefaultCustomCharacterDataRows = await Connection.QueryAsync(GenericQueries.GetDefaultCustomCharacterDataByDefaultSetName, parameters, commandType: CommandType.Text); } diff --git a/src/OWSData/Repositories/Implementations/MySQL/CharactersRepository.cs b/src/OWSData/Repositories/Implementations/MySQL/CharactersRepository.cs index 58657e58d..70ccb1486 100644 --- a/src/OWSData/Repositories/Implementations/MySQL/CharactersRepository.cs +++ b/src/OWSData/Repositories/Implementations/MySQL/CharactersRepository.cs @@ -220,7 +220,7 @@ public async Task> GetDefaultCustomCharacterData( parameters.Add("@CustomerGUID", customerGUID); parameters.Add("@DefaultSetName", defaultSetName); - outputDefaultCustomCharacterDataRows = await Connection.QueryAsync(GenericQueries.GetDefaultCharacterCustomDataByName, + outputDefaultCustomCharacterDataRows = await Connection.QueryAsync(GenericQueries.GetDefaultCustomCharacterDataByDefaultSetName, parameters, commandType: CommandType.Text); } diff --git a/src/OWSData/Repositories/Implementations/Postgres/CharactersRepository.cs b/src/OWSData/Repositories/Implementations/Postgres/CharactersRepository.cs index dfc83da10..f2d63b3be 100644 --- a/src/OWSData/Repositories/Implementations/Postgres/CharactersRepository.cs +++ b/src/OWSData/Repositories/Implementations/Postgres/CharactersRepository.cs @@ -30,47 +30,52 @@ public CharactersRepository(IOptions storageOptions) public async Task AddCharacterToMapInstanceByCharName(Guid customerGUID, string characterName, int mapInstanceID) { - IDbConnection conn = Connection; - conn.Open(); - using IDbTransaction transaction = conn.BeginTransaction(); - try + using (Connection) { - var parameters = new DynamicParameters(); - parameters.Add("@CustomerGUID", customerGUID); - parameters.Add("@CharName", characterName); - parameters.Add("@MapInstanceID", mapInstanceID); + using (IDbTransaction transaction = Connection.BeginTransaction()) + { + try + { + var parameters = new DynamicParameters(); + parameters.Add("@CustomerGUID", customerGUID); + parameters.Add("@CharName", characterName); + parameters.Add("@MapInstanceID", mapInstanceID); - var outputCharacter = await Connection.QuerySingleOrDefaultAsync(GenericQueries.GetCharacterIDByName, - parameters, - commandType: CommandType.Text); + var outputCharacter = await Connection.QuerySingleOrDefaultAsync( + GenericQueries.GetCharacterIDByName, + parameters, + commandType: CommandType.Text); - var outputZone = await Connection.QuerySingleOrDefaultAsync(GenericQueries.GetZoneName, - parameters, - commandType: CommandType.Text); + var outputZone = await Connection.QuerySingleOrDefaultAsync(GenericQueries.GetZoneName, + parameters, + commandType: CommandType.Text); - if (outputCharacter.CharacterId > 0) - { - parameters.Add("@CharacterID", outputCharacter.CharacterId); - parameters.Add("@ZoneName", outputZone.ZoneName); + if (outputCharacter.CharacterId > 0) + { + parameters.Add("@CharacterID", outputCharacter.CharacterId); + parameters.Add("@ZoneName", outputZone.ZoneName); - await Connection.ExecuteAsync(GenericQueries.RemoveCharacterFromAllInstances, - parameters, - commandType: CommandType.Text); + await Connection.ExecuteAsync(GenericQueries.RemoveCharacterFromAllInstances, + parameters, + commandType: CommandType.Text); - await Connection.ExecuteAsync(GenericQueries.AddCharacterToInstance, - parameters, - commandType: CommandType.Text); + await Connection.ExecuteAsync(GenericQueries.AddCharacterToInstance, + parameters, + commandType: CommandType.Text); - await Connection.ExecuteAsync(GenericQueries.UpdateCharacterZone, - parameters, - commandType: CommandType.Text); + await Connection.ExecuteAsync(GenericQueries.UpdateCharacterZone, + parameters, + commandType: CommandType.Text); + } + + transaction.Commit(); + } + catch + { + transaction.Rollback(); + throw new Exception("Database Exception in AddCharacterToMapInstanceByCharName!"); + } } - transaction.Commit(); - } - catch - { - transaction.Rollback(); - throw new Exception("Database Exception in AddCharacterToMapInstanceByCharName!"); } } @@ -135,43 +140,48 @@ public async Task CheckMapInstanceStatus(Guid customerGUID, int ma public async Task CleanUpInstances(Guid customerGUID) { - IDbConnection conn = Connection; - conn.Open(); - using IDbTransaction transaction = conn.BeginTransaction(); - try + using (Connection) { - var parameters = new DynamicParameters(); - parameters.Add("@CustomerGUID", customerGUID); - parameters.Add("@CharacterMinutes", 1); // TODO Add Configuration Parameter - parameters.Add("@MapMinutes", 2); // TODO Add Configuration Parameter + using (IDbTransaction transaction = Connection.BeginTransaction()) + { + try + { + var parameters = new DynamicParameters(); + parameters.Add("@CustomerGUID", customerGUID); + parameters.Add("@CharacterMinutes", 1); // TODO Add Configuration Parameter + parameters.Add("@MapMinutes", 2); // TODO Add Configuration Parameter - await transaction.ExecuteAsync(PostgresQueries.RemoveCharactersFromAllInactiveInstances, - parameters, - commandType: CommandType.Text); + await transaction.ExecuteAsync(PostgresQueries.RemoveCharactersFromAllInactiveInstances, + parameters, + commandType: CommandType.Text); - var outputMapInstances = await transaction.QueryAsync(PostgresQueries.GetAllInactiveMapInstances, - parameters, - commandType: CommandType.Text); + var outputMapInstances = await transaction.QueryAsync( + PostgresQueries.GetAllInactiveMapInstances, + parameters, + commandType: CommandType.Text); - if (outputMapInstances.Any()) - { - parameters.Add("@MapInstances", outputMapInstances); + if (outputMapInstances.Any()) + { + parameters.Add("@MapInstances", outputMapInstances); - await transaction.ExecuteAsync(PostgresQueries.RemoveCharacterFromInstances, - parameters, - commandType: CommandType.Text); + await transaction.ExecuteAsync(PostgresQueries.RemoveCharacterFromInstances, + parameters, + commandType: CommandType.Text); - await transaction.ExecuteAsync(PostgresQueries.RemoveMapInstances, - parameters, - commandType: CommandType.Text); + await transaction.ExecuteAsync(PostgresQueries.RemoveMapInstances, + parameters, + commandType: CommandType.Text); + } + + transaction.Commit(); + } + catch + { + transaction.Rollback(); + throw new Exception("Database Exception in CleanUpInstances!"); + } } - transaction.Commit(); - } - catch - { - transaction.Rollback(); - throw new Exception("Database Exception in CleanUpInstances!"); } } @@ -247,6 +257,21 @@ public async Task JoinMapByCharName(Guid customerGUID, string bool enableAutoLoopback = false; bool noPortForwarding = false; + outputObject = new JoinMapByCharName() + { + ServerIP = serverIp, + Port = port, + WorldServerID = -1, + WorldServerIP = worldServerIp, + WorldServerPort = worldServerPort, + MapInstanceID = mapInstanceID, + MapNameToStart = mapNameToStart, + MapInstanceStatus = -1, + NeedToStartupMap = false, + EnableAutoLoopback = false, + NoPortForwarding = false + }; + using (Connection) { var parameters = new DynamicParameters(); @@ -259,29 +284,31 @@ public async Task JoinMapByCharName(Guid customerGUID, string parameters, commandType: CommandType.Text); + if (outputMap == null) + { + Console.WriteLine($"CharactersRepository: JoinMapByCharName - Unable to find Zone Name: {zoneName} for CustomerGUID: {customerGUID} Check your Maps table for this row!"); + + return outputObject; + } + Characters outputCharacter = await Connection.QuerySingleOrDefaultAsync(GenericQueries.GetCharacterByName, parameters, commandType: CommandType.Text); + if (outputCharacter == null) + { + Console.WriteLine($"CharactersRepository: JoinMapByCharName - Unable to find Character by Name: {characterName} for CustomerGUID: {customerGUID}"); + + return outputObject; + } + Customers outputCustomer = await Connection.QuerySingleOrDefaultAsync(GenericQueries.GetCustomer, parameters, commandType: CommandType.Text); - if (outputCharacter == null) + if (outputCustomer == null) { - outputObject = new JoinMapByCharName() { - ServerIP = serverIp, - Port = port, - WorldServerID = -1, - WorldServerIP = worldServerIp, - WorldServerPort = worldServerPort, - MapInstanceID = mapInstanceID, - MapNameToStart = mapNameToStart, - MapInstanceStatus = -1, - NeedToStartupMap = false, - EnableAutoLoopback = enableAutoLoopback, - NoPortForwarding = noPortForwarding - }; + Console.WriteLine($"CharactersRepository: JoinMapByCharName - Unable to find Customer for CustomerGUID: {customerGUID}"); return outputObject; } @@ -372,14 +399,13 @@ public async Task SpinUpInstance(Guid customerGUID, string zoneNam if (outputWorldServers.Any()) { - int? firstAvailable = null; foreach (var worldServer in outputWorldServers) { var portsInUse = await Connection.QueryAsync(GenericQueries.GetPortsInUseByWorldServer, parameters, commandType: CommandType.Text); - firstAvailable = Enumerable.Range(worldServer.StartingMapInstancePort, worldServer.StartingMapInstancePort + worldServer.MaxNumberOfInstances) + int? firstAvailable = Enumerable.Range(worldServer.StartingMapInstancePort, worldServer.StartingMapInstancePort + worldServer.MaxNumberOfInstances) .Except(portsInUse) .FirstOrDefault(); @@ -394,11 +420,11 @@ public async Task SpinUpInstance(Guid customerGUID, string zoneNam parameters.Add("@MapID", outputMaps.MapId); parameters.Add("@Port", firstAvailable); - int outputMapInstanceID = await Connection.QuerySingleOrDefaultAsync(PostgresQueries.AddMapInstance, + int outputMapInstanceId = await Connection.QuerySingleOrDefaultAsync(PostgresQueries.AddMapInstance, parameters, commandType: CommandType.Text); - parameters.Add("@MapInstanceID", outputMapInstanceID); + parameters.Add("@MapInstanceID", outputMapInstanceId); MapInstances outputMapInstances = await Connection.QuerySingleOrDefaultAsync(GenericQueries.GetMapInstance, parameters, diff --git a/src/OWSData/Repositories/Implementations/Postgres/InstanceManagementRepository.cs b/src/OWSData/Repositories/Implementations/Postgres/InstanceManagementRepository.cs index 4793f850f..6e9b1df3c 100644 --- a/src/OWSData/Repositories/Implementations/Postgres/InstanceManagementRepository.cs +++ b/src/OWSData/Repositories/Implementations/Postgres/InstanceManagementRepository.cs @@ -121,34 +121,37 @@ await Connection.QueryFirstOrDefaultAsync(GenericQueries.UpdateMapInstanceStatus public async Task ShutDownWorldServer(Guid customerGUID, int worldServerID) { - IDbConnection conn = Connection; - conn.Open(); - using IDbTransaction transaction = conn.BeginTransaction(); - try - { - var parameter = new DynamicParameters(); - parameter.Add("@CustomerGUID", customerGUID); - parameter.Add("@WorldServerID", worldServerID); - parameter.Add("@ServerStatus", 0); - - await Connection.ExecuteAsync(GenericQueries.RemoveAllCharactersFromAllInstancesByWorldID, - parameter, - commandType: CommandType.Text); - - await Connection.ExecuteAsync(GenericQueries.RemoveAllMapInstancesForWorldServer, - parameter, - commandType: CommandType.Text); - - await Connection.ExecuteAsync(GenericQueries.UpdateWorldServerStatus, - parameter, - commandType: CommandType.Text); - - transaction.Commit(); - } - catch + using (Connection) { - transaction.Rollback(); - throw new Exception("Database Exception in ShutDownWorldServer!"); + using (IDbTransaction transaction = Connection.BeginTransaction()) + { + try + { + var parameter = new DynamicParameters(); + parameter.Add("@CustomerGUID", customerGUID); + parameter.Add("@WorldServerID", worldServerID); + parameter.Add("@ServerStatus", 0); + + await Connection.ExecuteAsync(GenericQueries.RemoveAllCharactersFromAllInstancesByWorldID, + parameter, + commandType: CommandType.Text); + + await Connection.ExecuteAsync(GenericQueries.RemoveAllMapInstancesForWorldServer, + parameter, + commandType: CommandType.Text); + + await Connection.ExecuteAsync(GenericQueries.UpdateWorldServerStatus, + parameter, + commandType: CommandType.Text); + + transaction.Commit(); + } + catch + { + transaction.Rollback(); + throw new Exception("Database Exception in ShutDownWorldServer!"); + } + } } SuccessAndErrorMessage output = new SuccessAndErrorMessage() diff --git a/src/OWSData/Repositories/Implementations/Postgres/UsersRepository.cs b/src/OWSData/Repositories/Implementations/Postgres/UsersRepository.cs index 9f5ceac24..fb7e3efc7 100644 --- a/src/OWSData/Repositories/Implementations/Postgres/UsersRepository.cs +++ b/src/OWSData/Repositories/Implementations/Postgres/UsersRepository.cs @@ -81,37 +81,41 @@ public async Task CreateCharacterUsingDefaultCharacterVa { SuccessAndErrorMessage outputObject = new SuccessAndErrorMessage(); - IDbConnection conn = Connection; - conn.Open(); - using IDbTransaction transaction = conn.BeginTransaction(); - try - { - var parameters = new DynamicParameters(); - parameters.Add("CustomerGUID", customerGUID); - parameters.Add("UserGUID", userGUID); - parameters.Add("CharacterName", characterName); - parameters.Add("DefaultSetName", defaultSetName); - - int outputCharacterId = await Connection.QuerySingleOrDefaultAsync(PostgresQueries.AddCharacterUsingDefaultCharacterValues, - parameters, - commandType: CommandType.Text); - - parameters.Add("CharacterID", outputCharacterId); - await Connection.ExecuteAsync(GenericQueries.AddDefaultCustomCharacterData, - parameters, - commandType: CommandType.Text); - transaction.Commit(); - } - catch (Exception ex) + using (Connection) { - transaction.Rollback(); - outputObject = new SuccessAndErrorMessage() + using (IDbTransaction transaction = Connection.BeginTransaction()) { - Success = false, - ErrorMessage = ex.Message - }; - - return outputObject; + try + { + var parameters = new DynamicParameters(); + parameters.Add("CustomerGUID", customerGUID); + parameters.Add("UserGUID", userGUID); + parameters.Add("CharacterName", characterName); + parameters.Add("DefaultSetName", defaultSetName); + + int outputCharacterId = await Connection.QuerySingleOrDefaultAsync( + PostgresQueries.AddCharacterUsingDefaultCharacterValues, + parameters, + commandType: CommandType.Text); + + parameters.Add("CharacterID", outputCharacterId); + await Connection.ExecuteAsync(GenericQueries.AddDefaultCustomCharacterData, + parameters, + commandType: CommandType.Text); + transaction.Commit(); + } + catch (Exception ex) + { + transaction.Rollback(); + outputObject = new SuccessAndErrorMessage() + { + Success = false, + ErrorMessage = ex.Message + }; + + return outputObject; + } + } } outputObject = new SuccessAndErrorMessage() diff --git a/src/OWSData/SQL/GenericQueries.cs b/src/OWSData/SQL/GenericQueries.cs index bf4c654af..d2ccd0021 100644 --- a/src/OWSData/SQL/GenericQueries.cs +++ b/src/OWSData/SQL/GenericQueries.cs @@ -123,12 +123,6 @@ FROM CustomCharacterData CCD WHERE CCD.CustomerGUID = @CustomerGUID AND C.CharName = @CharName"; - public static readonly string GetDefaultCharacterCustomDataByName = @"SELECT * - FROM CustomCharacterData CCD - INNER JOIN Characters C ON C.CharacterID = CCD.CharacterID - WHERE CCD.CustomerGUID = @CustomerGUID - AND C.CharName = @DefaultSetName"; - public static readonly string GetDefaultCustomCharacterDataByDefaultSetName = @"SELECT * FROM DefaultCustomCharacterData DCCD INNER JOIN DefaultCharacterValues DCV diff --git a/src/OWSGlobalData/Startup.cs b/src/OWSGlobalData/Startup.cs index 3bbb83316..694a10bbe 100644 --- a/src/OWSGlobalData/Startup.cs +++ b/src/OWSGlobalData/Startup.cs @@ -133,13 +133,13 @@ private void InitializeContainer(IServiceCollection services) switch (dbBackend) { case "postgres": - container.Register(Lifestyle.Scoped); + container.Register(Lifestyle.Singleton); break; case "mysql": - container.Register(Lifestyle.Scoped); + container.Register(Lifestyle.Singleton); break; default: // Default to MSSQL - container.Register(Lifestyle.Scoped); + container.Register(Lifestyle.Singleton); break; } } diff --git a/src/OWSInstanceManagement/Startup.cs b/src/OWSInstanceManagement/Startup.cs index 7489270f6..25d895fca 100644 --- a/src/OWSInstanceManagement/Startup.cs +++ b/src/OWSInstanceManagement/Startup.cs @@ -139,19 +139,19 @@ private void InitializeContainer(IServiceCollection services) switch (dbBackend) { case "postgres": - container.Register(Lifestyle.Scoped); - container.Register(Lifestyle.Scoped); - container.Register(Lifestyle.Scoped); + container.Register(Lifestyle.Singleton); + container.Register(Lifestyle.Singleton); + container.Register(Lifestyle.Singleton); break; case "mysql": - container.Register(Lifestyle.Scoped); - container.Register(Lifestyle.Scoped); - container.Register(Lifestyle.Scoped); + container.Register(Lifestyle.Singleton); + container.Register(Lifestyle.Singleton); + container.Register(Lifestyle.Singleton); break; default: // Default to MSSQL - container.Register(Lifestyle.Scoped); - container.Register(Lifestyle.Scoped); - container.Register(Lifestyle.Scoped); + container.Register(Lifestyle.Singleton); + container.Register(Lifestyle.Singleton); + container.Register(Lifestyle.Singleton); break; } } diff --git a/src/OWSPublicAPI/Startup.cs b/src/OWSPublicAPI/Startup.cs index 75cfdc880..9c03e130a 100644 --- a/src/OWSPublicAPI/Startup.cs +++ b/src/OWSPublicAPI/Startup.cs @@ -171,16 +171,16 @@ private void InitializeContainer(IServiceCollection services) switch (dbBackend) { case "postgres": - container.Register(Lifestyle.Transient); - container.Register(Lifestyle.Transient); + container.Register(Lifestyle.Singleton); + container.Register(Lifestyle.Singleton); break; case "mysql": - container.Register(Lifestyle.Transient); - container.Register(Lifestyle.Transient); + container.Register(Lifestyle.Singleton); + container.Register(Lifestyle.Singleton); break; default: // Default to MSSQL - container.Register(Lifestyle.Transient); - container.Register(Lifestyle.Transient); + container.Register(Lifestyle.Singleton); + container.Register(Lifestyle.Singleton); break; } }