From f4b160b73567a0da3d60701e0fa789d46ed0fad5 Mon Sep 17 00:00:00 2001 From: RaidMax Date: Fri, 28 Jan 2022 09:35:01 -0600 Subject: [PATCH] small startup performance optimization --- Application/ApplicationManager.cs | 8 ++--- .../Factories/ConfigurationHandlerFactory.cs | 15 ++++++-- Application/IW4MServer.cs | 2 +- Application/Main.cs | 8 +++-- Application/Misc/BaseConfigurationHandler.cs | 36 ++++++++++--------- Application/Misc/ScriptPlugin.cs | 11 ++++-- .../Misc/ScriptPluginConfigurationWrapper.cs | 13 ++++--- Data/Context/ContextSeed.cs | 6 ++-- Data/Helpers/LookupCache.cs | 4 +-- .../AutomessageFeed/AutomessageFeed.csproj | 2 +- Plugins/AutomessageFeed/Plugin.cs | 1 + .../IW4ScriptCommands.csproj | 2 +- .../LiveRadar/Controllers/RadarController.cs | 24 +++++++------ Plugins/LiveRadar/LiveRadar.csproj | 2 +- Plugins/LiveRadar/Plugin.cs | 1 + Plugins/Login/Login.csproj | 2 +- Plugins/Login/Plugin.cs | 1 + Plugins/ProfanityDeterment/Plugin.cs | 1 + .../ProfanityDeterment.csproj | 2 +- .../Client/ServerDistributionCalculator.cs | 2 ++ Plugins/Stats/Plugin.cs | 1 + Plugins/Stats/Stats.csproj | 2 +- Plugins/Welcome/Plugin.cs | 1 + Plugins/Welcome/Welcome.csproj | 2 +- .../Interfaces/IBaseConfiguration.cs | 2 +- .../Interfaces/IConfigurationHandler.cs | 4 +-- .../IConfigurationHandlerFactory.cs | 20 ++++++++--- SharedLibraryCore/SharedLibraryCore.csproj | 4 +-- 28 files changed, 112 insertions(+), 67 deletions(-) diff --git a/Application/ApplicationManager.cs b/Application/ApplicationManager.cs index 37086a546..fc72178f0 100644 --- a/Application/ApplicationManager.cs +++ b/Application/ApplicationManager.cs @@ -355,10 +355,10 @@ namespace IW4MAdmin.Application // copy over default config if it doesn't exist if (!_appConfig.Servers?.Any() ?? true) { - var defaultConfig = new BaseConfigurationHandler("DefaultSettings").Configuration(); - //ConfigHandler.Set((ApplicationConfiguration)new ApplicationConfiguration().Generate()); - //var newConfig = ConfigHandler.Configuration(); - + var defaultHandler = new BaseConfigurationHandler("DefaultSettings"); + await defaultHandler.BuildAsync(); + var defaultConfig = defaultHandler.Configuration(); + _appConfig.AutoMessages = defaultConfig.AutoMessages; _appConfig.GlobalRules = defaultConfig.GlobalRules; _appConfig.DisallowedClientNames = defaultConfig.DisallowedClientNames; diff --git a/Application/Factories/ConfigurationHandlerFactory.cs b/Application/Factories/ConfigurationHandlerFactory.cs index 6f2c8ec16..8da9f7023 100644 --- a/Application/Factories/ConfigurationHandlerFactory.cs +++ b/Application/Factories/ConfigurationHandlerFactory.cs @@ -1,4 +1,5 @@ -using IW4MAdmin.Application.Misc; +using System.Threading.Tasks; +using IW4MAdmin.Application.Misc; using SharedLibraryCore.Interfaces; namespace IW4MAdmin.Application.Factories @@ -17,7 +18,17 @@ namespace IW4MAdmin.Application.Factories /// public IConfigurationHandler GetConfigurationHandler(string name) where T : IBaseConfiguration { - return new BaseConfigurationHandler(name); + var handler = new BaseConfigurationHandler(name); + handler.BuildAsync().Wait(); + return handler; + } + + /// + public async Task> GetConfigurationHandlerAsync(string name) where T : IBaseConfiguration + { + var handler = new BaseConfigurationHandler(name); + await handler.BuildAsync(); + return handler; } } } diff --git a/Application/IW4MServer.cs b/Application/IW4MServer.cs index 9ec82a5c4..b21d174cb 100644 --- a/Application/IW4MServer.cs +++ b/Application/IW4MServer.cs @@ -355,7 +355,7 @@ namespace IW4MAdmin try { var factory = _serviceProvider.GetRequiredService(); - await using var context = factory.CreateContext(); + await using var context = factory.CreateContext(enableTracking: false); var messageCount = await context.InboxMessages .CountAsync(msg => msg.DestinationClientId == E.Origin.ClientId && !msg.IsDelivered); diff --git a/Application/Main.cs b/Application/Main.cs index 481464662..de8c1601f 100644 --- a/Application/Main.cs +++ b/Application/Main.cs @@ -341,7 +341,11 @@ namespace IW4MAdmin.Application // setup the static resources (config/master api/translations) var serviceCollection = new ServiceCollection(); var appConfigHandler = new BaseConfigurationHandler("IW4MAdminSettings"); + await appConfigHandler.BuildAsync(); var defaultConfigHandler = new BaseConfigurationHandler("DefaultSettings"); + await defaultConfigHandler.BuildAsync(); + var commandConfigHandler = new BaseConfigurationHandler("CommandConfiguration"); + await commandConfigHandler.BuildAsync(); var defaultConfig = defaultConfigHandler.Configuration(); var appConfig = appConfigHandler.Configuration(); var masterUri = Utilities.IsDevelopment @@ -380,9 +384,7 @@ namespace IW4MAdmin.Application .AddSingleton(serviceCollection) .AddSingleton, BaseConfigurationHandler>() .AddSingleton((IConfigurationHandler) appConfigHandler) - .AddSingleton( - new BaseConfigurationHandler("CommandConfiguration") as - IConfigurationHandler) + .AddSingleton>(commandConfigHandler) .AddSingleton(appConfig) .AddSingleton(serviceProvider => serviceProvider.GetRequiredService>() diff --git a/Application/Misc/BaseConfigurationHandler.cs b/Application/Misc/BaseConfigurationHandler.cs index 95fc2e36b..fc93bd47f 100644 --- a/Application/Misc/BaseConfigurationHandler.cs +++ b/Application/Misc/BaseConfigurationHandler.cs @@ -1,11 +1,13 @@ -using Newtonsoft.Json; -using SharedLibraryCore; +using SharedLibraryCore; using SharedLibraryCore.Exceptions; using SharedLibraryCore.Interfaces; using System; using System.IO; +using System.Text.Json; +using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; +using JsonSerializer = System.Text.Json.JsonSerializer; namespace IW4MAdmin.Application.Misc { @@ -15,19 +17,24 @@ namespace IW4MAdmin.Application.Misc /// base configuration type public class BaseConfigurationHandler : IConfigurationHandler where T : IBaseConfiguration { - T _configuration; + private T _configuration; private readonly SemaphoreSlim _onSaving; + private readonly JsonSerializerOptions _serializerOptions; - public BaseConfigurationHandler(string fn) + + public BaseConfigurationHandler(string fileName) { + _serializerOptions = new JsonSerializerOptions + { + WriteIndented = true, + }; + _serializerOptions.Converters.Add(new JsonStringEnumConverter()); _onSaving = new SemaphoreSlim(1, 1); - FileName = Path.Join(Utilities.OperatingDirectory, "Configuration", $"{fn}.json"); - Build(); + FileName = Path.Join(Utilities.OperatingDirectory, "Configuration", $"{fileName}.json"); } public BaseConfigurationHandler() : this(typeof(T).Name) { - _onSaving = new SemaphoreSlim(1, 1); } ~BaseConfigurationHandler() @@ -37,12 +44,12 @@ namespace IW4MAdmin.Application.Misc public string FileName { get; } - public void Build() + public async Task BuildAsync() { try { - var configContent = File.ReadAllText(FileName); - _configuration = JsonConvert.DeserializeObject(configContent); + await using var fileStream = File.OpenRead(FileName); + _configuration = await JsonSerializer.DeserializeAsync(fileStream, _serializerOptions); } catch (FileNotFoundException) @@ -65,14 +72,9 @@ namespace IW4MAdmin.Application.Misc try { await _onSaving.WaitAsync(); - var settings = new JsonSerializerSettings() - { - Formatting = Formatting.Indented - }; - settings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter()); - var appConfigJson = JsonConvert.SerializeObject(_configuration, settings); - await File.WriteAllTextAsync(FileName, appConfigJson); + await using var fileStream = File.OpenWrite(FileName); + await JsonSerializer.SerializeAsync(fileStream, _configuration, _serializerOptions); } finally diff --git a/Application/Misc/ScriptPlugin.cs b/Application/Misc/ScriptPlugin.cs index 0b8509e64..dfbf91f83 100644 --- a/Application/Misc/ScriptPlugin.cs +++ b/Application/Misc/ScriptPlugin.cs @@ -168,9 +168,6 @@ namespace IW4MAdmin.Application.Misc } } - _scriptEngine.SetValue("_configHandler", new ScriptPluginConfigurationWrapper(Name, _scriptEngine)); - await OnLoadAsync(manager); - try { if (pluginObject.isParser) @@ -181,6 +178,14 @@ namespace IW4MAdmin.Application.Misc manager.AdditionalEventParsers.Add(eventParser); manager.AdditionalRConParsers.Add(rconParser); } + + else + { + var configWrapper = new ScriptPluginConfigurationWrapper(Name, _scriptEngine); + await configWrapper.InitializeAsync(); + _scriptEngine.SetValue("_configHandler", configWrapper); + await OnLoadAsync(manager); + } } catch (RuntimeBinderException) { } diff --git a/Application/Misc/ScriptPluginConfigurationWrapper.cs b/Application/Misc/ScriptPluginConfigurationWrapper.cs index 11d580716..b511a3057 100644 --- a/Application/Misc/ScriptPluginConfigurationWrapper.cs +++ b/Application/Misc/ScriptPluginConfigurationWrapper.cs @@ -12,19 +12,24 @@ namespace IW4MAdmin.Application.Misc public class ScriptPluginConfigurationWrapper { private readonly BaseConfigurationHandler _handler; - private readonly ScriptPluginConfiguration _config; + private ScriptPluginConfiguration _config; private readonly string _pluginName; private readonly Engine _scriptEngine; public ScriptPluginConfigurationWrapper(string pluginName, Engine scriptEngine) { _handler = new BaseConfigurationHandler("ScriptPluginSettings"); - _config = _handler.Configuration() ?? - (ScriptPluginConfiguration) new ScriptPluginConfiguration().Generate(); _pluginName = pluginName; _scriptEngine = scriptEngine; } + public async Task InitializeAsync() + { + await _handler.BuildAsync(); + _config = _handler.Configuration() ?? + (ScriptPluginConfiguration) new ScriptPluginConfiguration().Generate(); + } + private static int? AsInteger(double d) { return int.TryParse(d.ToString(CultureInfo.InvariantCulture), out var parsed) ? parsed : (int?) null; @@ -87,4 +92,4 @@ namespace IW4MAdmin.Application.Misc return JsValue.FromObject(_scriptEngine, item); } } -} \ No newline at end of file +} diff --git a/Data/Context/ContextSeed.cs b/Data/Context/ContextSeed.cs index c9acf06f9..c05a95e38 100644 --- a/Data/Context/ContextSeed.cs +++ b/Data/Context/ContextSeed.cs @@ -23,7 +23,7 @@ namespace Data.Context { var link = new EFAliasLink(); - context.Clients.Add(new EFClient() + context.Clients.Add(new EFClient { Active = false, Connections = 0, @@ -33,7 +33,7 @@ namespace Data.Context Masked = true, NetworkId = 0, AliasLink = link, - CurrentAlias = new EFAlias() + CurrentAlias = new EFAlias { Link = link, Active = true, @@ -46,4 +46,4 @@ namespace Data.Context } } } -} \ No newline at end of file +} diff --git a/Data/Helpers/LookupCache.cs b/Data/Helpers/LookupCache.cs index fd45b8bcb..1a6f06e4a 100644 --- a/Data/Helpers/LookupCache.cs +++ b/Data/Helpers/LookupCache.cs @@ -102,7 +102,7 @@ namespace Data.Helpers { try { - await using var context = _contextFactory.CreateContext(); + await using var context = _contextFactory.CreateContext(false); _cachedItems = await context.Set().ToDictionaryAsync(item => item.Id); } catch (Exception ex) @@ -111,4 +111,4 @@ namespace Data.Helpers } } } -} \ No newline at end of file +} diff --git a/Plugins/AutomessageFeed/AutomessageFeed.csproj b/Plugins/AutomessageFeed/AutomessageFeed.csproj index d371859c9..6300d4b00 100644 --- a/Plugins/AutomessageFeed/AutomessageFeed.csproj +++ b/Plugins/AutomessageFeed/AutomessageFeed.csproj @@ -10,7 +10,7 @@ - + diff --git a/Plugins/AutomessageFeed/Plugin.cs b/Plugins/AutomessageFeed/Plugin.cs index bb43a00d7..99df71171 100644 --- a/Plugins/AutomessageFeed/Plugin.cs +++ b/Plugins/AutomessageFeed/Plugin.cs @@ -65,6 +65,7 @@ namespace AutomessageFeed public async Task OnLoadAsync(IManager manager) { + await _configurationHandler.BuildAsync(); if (_configurationHandler.Configuration() == null) { _configurationHandler.Set((Configuration)new Configuration().Generate()); diff --git a/Plugins/IW4ScriptCommands/IW4ScriptCommands.csproj b/Plugins/IW4ScriptCommands/IW4ScriptCommands.csproj index 6527c6fcb..bf99ae8f3 100644 --- a/Plugins/IW4ScriptCommands/IW4ScriptCommands.csproj +++ b/Plugins/IW4ScriptCommands/IW4ScriptCommands.csproj @@ -10,7 +10,7 @@ - + diff --git a/Plugins/LiveRadar/Controllers/RadarController.cs b/Plugins/LiveRadar/Controllers/RadarController.cs index f45a970d8..56a620375 100644 --- a/Plugins/LiveRadar/Controllers/RadarController.cs +++ b/Plugins/LiveRadar/Controllers/RadarController.cs @@ -1,29 +1,25 @@ using LiveRadar.Configuration; using Microsoft.AspNetCore.Mvc; -using Newtonsoft.Json; using SharedLibraryCore; using SharedLibraryCore.Dtos; using SharedLibraryCore.Interfaces; using System.Linq; +using System.Threading.Tasks; using Microsoft.AspNetCore.Http; namespace LiveRadar.Web.Controllers { public class RadarController : BaseController { - private static readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings() - { - ReferenceLoopHandling = ReferenceLoopHandling.Ignore, - ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver() - }; - private readonly IManager _manager; - private readonly LiveRadarConfiguration _config; + private static LiveRadarConfiguration _config; + private readonly IConfigurationHandler _configurationHandler; public RadarController(IManager manager, IConfigurationHandlerFactory configurationHandlerFactory) : base(manager) { _manager = manager; - _config = configurationHandlerFactory.GetConfigurationHandler("LiveRadarConfiguration").Configuration() ?? new LiveRadarConfiguration(); + _configurationHandler = + configurationHandlerFactory.GetConfigurationHandler("LiveRadarConfiguration"); } [HttpGet] @@ -46,7 +42,7 @@ namespace LiveRadar.Web.Controllers [HttpGet] [Route("Radar/{serverId}/Map")] - public IActionResult Map(long? serverId = null) + public async Task Map(long? serverId = null) { var server = serverId == null ? _manager.GetServers().FirstOrDefault() : _manager.GetServers().FirstOrDefault(_server => _server.EndPoint == serverId); @@ -54,6 +50,12 @@ namespace LiveRadar.Web.Controllers { return NotFound(); } + + if (_config == null) + { + await _configurationHandler.BuildAsync(); + _config = _configurationHandler.Configuration() ?? new LiveRadarConfiguration(); + } var map = _config.Maps.FirstOrDefault(_map => _map.Name == server.CurrentMap.Name); @@ -93,4 +95,4 @@ namespace LiveRadar.Web.Controllers return Ok(); } } -} \ No newline at end of file +} diff --git a/Plugins/LiveRadar/LiveRadar.csproj b/Plugins/LiveRadar/LiveRadar.csproj index c92da8c81..d2a92f373 100644 --- a/Plugins/LiveRadar/LiveRadar.csproj +++ b/Plugins/LiveRadar/LiveRadar.csproj @@ -23,7 +23,7 @@ - + diff --git a/Plugins/LiveRadar/Plugin.cs b/Plugins/LiveRadar/Plugin.cs index 7c921a147..c7aae558a 100644 --- a/Plugins/LiveRadar/Plugin.cs +++ b/Plugins/LiveRadar/Plugin.cs @@ -103,6 +103,7 @@ namespace LiveRadar public async Task OnLoadAsync(IManager manager) { + await _configurationHandler.BuildAsync(); if (_configurationHandler.Configuration() == null) { _configurationHandler.Set((LiveRadarConfiguration)new LiveRadarConfiguration().Generate()); diff --git a/Plugins/Login/Login.csproj b/Plugins/Login/Login.csproj index 6ba24dd5d..a1a53cf39 100644 --- a/Plugins/Login/Login.csproj +++ b/Plugins/Login/Login.csproj @@ -19,7 +19,7 @@ - + diff --git a/Plugins/Login/Plugin.cs b/Plugins/Login/Plugin.cs index 7d7936c2b..b5a0d8eac 100644 --- a/Plugins/Login/Plugin.cs +++ b/Plugins/Login/Plugin.cs @@ -76,6 +76,7 @@ namespace IW4MAdmin.Plugins.Login { AuthorizedClients = new ConcurrentDictionary(); + await _configHandler.BuildAsync(); if (_configHandler.Configuration() == null) { _configHandler.Set((Configuration)new Configuration().Generate()); diff --git a/Plugins/ProfanityDeterment/Plugin.cs b/Plugins/ProfanityDeterment/Plugin.cs index be0f752b4..36697d1c5 100644 --- a/Plugins/ProfanityDeterment/Plugin.cs +++ b/Plugins/ProfanityDeterment/Plugin.cs @@ -109,6 +109,7 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment public async Task OnLoadAsync(IManager manager) { + await _configHandler.BuildAsync(); if (_configHandler.Configuration() == null) { _configHandler.Set((Configuration)new Configuration().Generate()); diff --git a/Plugins/ProfanityDeterment/ProfanityDeterment.csproj b/Plugins/ProfanityDeterment/ProfanityDeterment.csproj index 95a2497b5..24ece7026 100644 --- a/Plugins/ProfanityDeterment/ProfanityDeterment.csproj +++ b/Plugins/ProfanityDeterment/ProfanityDeterment.csproj @@ -16,7 +16,7 @@ - + diff --git a/Plugins/Stats/Client/ServerDistributionCalculator.cs b/Plugins/Stats/Client/ServerDistributionCalculator.cs index b7c9bfc57..7d9d0f7c2 100644 --- a/Plugins/Stats/Client/ServerDistributionCalculator.cs +++ b/Plugins/Stats/Client/ServerDistributionCalculator.cs @@ -48,6 +48,7 @@ namespace Stats.Client await LoadServers(); _distributionCache.SetCacheItem((async (set, token) => { + await _configurationHandler.BuildAsync(); var validPlayTime = _configurationHandler.Configuration()?.TopPlayersMinPlayTime ?? 3600 * 3; var distributions = new Dictionary(); @@ -73,6 +74,7 @@ namespace Stats.Client _maxZScoreCache.SetCacheItem(async (set, token) => { + await _configurationHandler.BuildAsync(); var validPlayTime = _configurationHandler.Configuration()?.TopPlayersMinPlayTime ?? 3600 * 3; var zScore = await set diff --git a/Plugins/Stats/Plugin.cs b/Plugins/Stats/Plugin.cs index fa226571d..2266e3822 100644 --- a/Plugins/Stats/Plugin.cs +++ b/Plugins/Stats/Plugin.cs @@ -172,6 +172,7 @@ namespace IW4MAdmin.Plugins.Stats public async Task OnLoadAsync(IManager manager) { + await Config.BuildAsync(); // load custom configuration if (Config.Configuration() == null) { diff --git a/Plugins/Stats/Stats.csproj b/Plugins/Stats/Stats.csproj index 528984475..38a68d744 100644 --- a/Plugins/Stats/Stats.csproj +++ b/Plugins/Stats/Stats.csproj @@ -17,7 +17,7 @@ - + diff --git a/Plugins/Welcome/Plugin.cs b/Plugins/Welcome/Plugin.cs index e70c0c8d9..a0d790d0a 100644 --- a/Plugins/Welcome/Plugin.cs +++ b/Plugins/Welcome/Plugin.cs @@ -35,6 +35,7 @@ namespace IW4MAdmin.Plugins.Welcome public async Task OnLoadAsync(IManager manager) { + await _configHandler.BuildAsync(); if (_configHandler.Configuration() == null) { _configHandler.Set((WelcomeConfiguration) new WelcomeConfiguration().Generate()); diff --git a/Plugins/Welcome/Welcome.csproj b/Plugins/Welcome/Welcome.csproj index eb70f19ba..7afe29afb 100644 --- a/Plugins/Welcome/Welcome.csproj +++ b/Plugins/Welcome/Welcome.csproj @@ -20,7 +20,7 @@ - + diff --git a/SharedLibraryCore/Interfaces/IBaseConfiguration.cs b/SharedLibraryCore/Interfaces/IBaseConfiguration.cs index 5902dfa0a..6708c2a1d 100644 --- a/SharedLibraryCore/Interfaces/IBaseConfiguration.cs +++ b/SharedLibraryCore/Interfaces/IBaseConfiguration.cs @@ -5,4 +5,4 @@ string Name(); IBaseConfiguration Generate(); } -} \ No newline at end of file +} diff --git a/SharedLibraryCore/Interfaces/IConfigurationHandler.cs b/SharedLibraryCore/Interfaces/IConfigurationHandler.cs index 994d51faa..f6edb3dd0 100644 --- a/SharedLibraryCore/Interfaces/IConfigurationHandler.cs +++ b/SharedLibraryCore/Interfaces/IConfigurationHandler.cs @@ -6,8 +6,8 @@ namespace SharedLibraryCore.Interfaces { string FileName { get; } Task Save(); - void Build(); + Task BuildAsync(); T Configuration(); void Set(T config); } -} \ No newline at end of file +} diff --git a/SharedLibraryCore/Interfaces/IConfigurationHandlerFactory.cs b/SharedLibraryCore/Interfaces/IConfigurationHandlerFactory.cs index 0108ceecd..91a545c77 100644 --- a/SharedLibraryCore/Interfaces/IConfigurationHandlerFactory.cs +++ b/SharedLibraryCore/Interfaces/IConfigurationHandlerFactory.cs @@ -1,17 +1,27 @@ -namespace SharedLibraryCore.Interfaces +using System.Threading.Tasks; + +namespace SharedLibraryCore.Interfaces { /// - /// defines the capabilities of the configuration handler factory - /// used to generate new instance of configuration handlers + /// defines the capabilities of the configuration handler factory + /// used to generate new instance of configuration handlers /// public interface IConfigurationHandlerFactory { /// - /// generates a new configuration handler + /// generates a new configuration handler /// /// base configuration type /// file name of configuration /// new configuration handler instance IConfigurationHandler GetConfigurationHandler(string name) where T : IBaseConfiguration; + + /// + /// generates a new configuration handler and builds the configuration automatically + /// + /// base configuration type + /// file name of configuration + /// new configuration handler instance + Task> GetConfigurationHandlerAsync(string name) where T : IBaseConfiguration; } -} \ No newline at end of file +} diff --git a/SharedLibraryCore/SharedLibraryCore.csproj b/SharedLibraryCore/SharedLibraryCore.csproj index 8130b41c0..421f3c8fc 100644 --- a/SharedLibraryCore/SharedLibraryCore.csproj +++ b/SharedLibraryCore/SharedLibraryCore.csproj @@ -4,7 +4,7 @@ Library net6.0 RaidMax.IW4MAdmin.SharedLibraryCore - 2022.01.25.2 + 2022.01.28.1 RaidMax Forever None Debug;Release;Prerelease @@ -19,7 +19,7 @@ true MIT Shared Library for IW4MAdmin - 2022.01.25.2 + 2022.01.28.1