diff --git a/MareSynchronosServer/MareSynchronosServer/Startup.cs b/MareSynchronosServer/MareSynchronosServer/Startup.cs index d9b0bbe..090f783 100644 --- a/MareSynchronosServer/MareSynchronosServer/Startup.cs +++ b/MareSynchronosServer/MareSynchronosServer/Startup.cs @@ -18,6 +18,8 @@ using MareSynchronosShared.Utils; using MareSynchronosServer.Identity; using MareSynchronosShared.Services; using Prometheus; +using Microsoft.Extensions.Options; +using Grpc.Net.ClientFactory; namespace MareSynchronosServer; @@ -207,7 +209,7 @@ public class Startup }; }); - services.AddGrpcClient(c => + services.AddGrpcClient("MainServer", c => { c.Address = new Uri(mareConfig.GetValue(nameof(ServerConfiguration.MainServerGrpcAddress))); }).ConfigureChannel(c => @@ -221,8 +223,16 @@ public class Startup services.AddSingleton(); services.AddHostedService(p => p.GetService()); - services.AddSingleton, MareConfigurationServiceClient>(); - services.AddSingleton, MareConfigurationServiceClient>(); + services.AddSingleton>(c => new MareConfigurationServiceClient( + c.GetService>>(), + c.GetService>(), + c.GetService(), + "MainServer")); + services.AddSingleton>(c => new MareConfigurationServiceClient( + c.GetService>>(), + c.GetService>(), + c.GetService(), + "MainServer")); } else { diff --git a/MareSynchronosServer/MareSynchronosServices/Startup.cs b/MareSynchronosServer/MareSynchronosServices/Startup.cs index f84c440..1fce88a 100644 --- a/MareSynchronosServer/MareSynchronosServices/Startup.cs +++ b/MareSynchronosServer/MareSynchronosServices/Startup.cs @@ -7,6 +7,9 @@ using MareSynchronosShared.Utils; using Grpc.Net.Client.Configuration; using MareSynchronosShared.Protos; using MareSynchronosShared.Services; +using Grpc.Net.ClientFactory; +using Microsoft.Extensions.Options; +using Microsoft.Extensions.DependencyInjection; namespace MareSynchronosServices; @@ -33,7 +36,7 @@ public class Startup }, Configuration.GetValue(nameof(MareConfigurationBase.DbContextPoolSize), 1024)); services.AddSingleton(m => new MareMetrics(m.GetService>(), new List { }, - new List {})); + new List { })); var noRetryConfig = new MethodConfig { @@ -53,7 +56,7 @@ public class Startup }; }); - services.AddGrpcClient(c => + services.AddGrpcClient("MainServer", c => { c.Address = new Uri(mareConfig.GetValue(nameof(ServicesConfiguration.MainServerGrpcAddress))); }).ConfigureChannel(c => @@ -72,8 +75,16 @@ public class Startup services.AddSingleton(); services.AddHostedService(); services.AddSingleton, MareConfigurationServiceServer>(); - services.AddSingleton, MareConfigurationServiceClient>(); - services.AddSingleton, MareConfigurationServiceClient>(); + services.AddSingleton>(c => new MareConfigurationServiceClient( + c.GetService>>(), + c.GetService>(), + c.GetService(), + "MainServer")); + services.AddSingleton>(c => new MareConfigurationServiceClient( + c.GetService>>(), + c.GetService>(), + c.GetService(), + "MainServer")); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) diff --git a/MareSynchronosServer/MareSynchronosShared/Services/MareConfigurationServiceClient.cs b/MareSynchronosServer/MareSynchronosShared/Services/MareConfigurationServiceClient.cs index 580fdf0..f385692 100644 --- a/MareSynchronosServer/MareSynchronosShared/Services/MareConfigurationServiceClient.cs +++ b/MareSynchronosServer/MareSynchronosShared/Services/MareConfigurationServiceClient.cs @@ -18,20 +18,16 @@ public class MareConfigurationServiceClient : IConfigurationService where private readonly T _config; private readonly ConcurrentDictionary _cachedRemoteProperties = new(StringComparer.Ordinal); private readonly ILogger> _logger; - private readonly ConfigurationServiceClient _configurationServiceClient; - - public MareConfigurationServiceClient(ILogger> logger, IOptions config, ConfigurationServiceClient configurationServiceClient) - { - _config = config.Value; - _logger = logger; - _configurationServiceClient = configurationServiceClient; - } + private readonly GrpcClientFactory _grpcClientFactory; + private readonly string _grpcClientName; + private static SemaphoreSlim _readLock = new(1); public MareConfigurationServiceClient(ILogger> logger, IOptions config, GrpcClientFactory grpcClientFactory, string grpcClientName) { _config = config.Value; _logger = logger; - _configurationServiceClient = grpcClientFactory.CreateClient(grpcClientName); + _grpcClientFactory = grpcClientFactory; + _grpcClientName = grpcClientName; } public bool IsMain => false; @@ -44,35 +40,37 @@ public class MareConfigurationServiceClient : IConfigurationService where bool isRemote = prop.GetCustomAttributes(typeof(RemoteConfigurationAttribute), inherit: true).Any(); if (isRemote) { - bool isCurrent = false; - if (_cachedRemoteProperties.TryGetValue(key, out var existingEntry) && existingEntry.Inserted > DateTime.Now - TimeSpan.FromMinutes(30)) + _readLock.Wait(); + if (_cachedRemoteProperties.TryGetValue(key, out var existingEntry) && existingEntry.Inserted > DateTime.Now - TimeSpan.FromMinutes(60)) { - isCurrent = true; + _readLock.Release(); + return (T1)_cachedRemoteProperties[key].Value; } - if (!isCurrent) + try { - try + var result = GetValueFromGrpc(key, defaultValue, prop.PropertyType); + if (result == null) return defaultValue; + _cachedRemoteProperties[key] = result; + return (T1)_cachedRemoteProperties[key].Value; + } + catch (Exception ex) + { + if (existingEntry != null) { - var result = GetValueFromGrpc(key, defaultValue, prop.PropertyType); - if (result == null) return defaultValue; - _cachedRemoteProperties[key] = result; - return (T1)_cachedRemoteProperties[key].Value; + _logger.LogWarning(ex, "Could not get value for {key} from Grpc, returning existing", key); + return (T1)existingEntry.Value; } - catch (Exception ex) + else { - if (existingEntry != null) - { - _logger.LogWarning(ex, "Could not get value for {key} from Grpc, returning existing", key); - return (T1)existingEntry.Value; - } - else - { - _logger.LogWarning(ex, "Could not get value for {key} from Grpc, returning default", key); - return defaultValue; - } + _logger.LogWarning(ex, "Could not get value for {key} from Grpc, returning default", key); + return defaultValue; } } + finally + { + _readLock.Release(); + } } var value = prop.GetValue(_config); @@ -85,12 +83,14 @@ public class MareConfigurationServiceClient : IConfigurationService where try { _logger.LogInformation("Getting {key} from Grpc", key); - var response = _configurationServiceClient.GetConfigurationEntry(new KeyMessage { Key = key, Default = Convert.ToString(defaultValue, CultureInfo.InvariantCulture) }); + var configClient = _grpcClientFactory.CreateClient(_grpcClientName); + var response = configClient.GetConfigurationEntry(new KeyMessage { Key = key, Default = Convert.ToString(defaultValue, CultureInfo.InvariantCulture) }); _logger.LogInformation("Grpc Response for {key} = {value}", key, response.Value); return new RemoteCachedEntry(JsonSerializer.Deserialize(response.Value, t), DateTime.Now); } - catch + catch (Exception ex) { + _logger.LogWarning(ex, "Failure Getting Cached Entry"); return null; } } @@ -103,22 +103,37 @@ public class MareConfigurationServiceClient : IConfigurationService where bool isRemote = prop.GetCustomAttributes(typeof(RemoteConfigurationAttribute), inherit: true).Any(); if (isRemote) { - bool isCurrent = false; - if (_cachedRemoteProperties.TryGetValue(key, out var existingEntry) && existingEntry.Inserted > DateTime.Now - TimeSpan.FromMinutes(30)) + _readLock.Wait(); + if (_cachedRemoteProperties.TryGetValue(key, out var existingEntry) && existingEntry.Inserted > DateTime.Now - TimeSpan.FromMinutes(60)) { - isCurrent = true; + _readLock.Release(); + return (T1)_cachedRemoteProperties[key].Value; } - if (!isCurrent) + try { var result = GetValueFromGrpc(key, null, prop.PropertyType); if (result == null) throw new KeyNotFoundException(key); _cachedRemoteProperties[key] = result; + return (T1)_cachedRemoteProperties[key].Value; + } + catch (Exception ex) + { + if (existingEntry != null) + { + _logger.LogWarning(ex, "Could not get value for {key} from Grpc, returning existing", key); + return (T1)existingEntry.Value; + } + else + { + _logger.LogWarning(ex, "Could not get value for {key} from Grpc, throwing exception", key); + throw new KeyNotFoundException(key); + } + } + finally + { + _readLock.Release(); } - - if (!_cachedRemoteProperties.ContainsKey(key)) throw new KeyNotFoundException(key); - - return (T1)_cachedRemoteProperties[key].Value; } var value = prop.GetValue(_config);