Skip to content

Commit eeca03d

Browse files
authored
Configuration sync disposable config providers (#31)
* Sync all versions All packages are now on inline versioning. * #30: Sync Logging with EventLog ~ Convert LogLevel property into a getter for dynamic runtime fetching of the log level. ~ Add a fix on Windows for color logging failing by manually updating the console mode flags with ENABLE_VIRTUAL_TERMINAL_PROCESSING. ~ Rename LogLevel.Trace and ILogger.Trace to Verbose and add better docomments to each method and log level to better explain their use cases. ~ Remove ILogger.Log as this was technically an alias to ILogger.Information as it did the same thing. ~ Update all log methods to now take the raw message getter instead of formatting the messages themselves, this will help to introduce better performance by not evaluating the message straight away. ~ Add another ILogger.Error method with an argument to supply a message that precedes the exception trace back. * Update Configuration: Convert IVaultProvider to IDisposable Update current registered auto refresh providers to a dictionary to make concurrent removal easier. Expose the current cached values to inheritied classes. Convert mount and path to virutal to not introduce a breaking change by adding set to the mix. Move the refresh thread from a simple thread wait to a wait event system to allow manual refreshing of all global providers. Add argument to constructors to control if the provider should be added to the auto refresh thread or not. Add extra constructor to allow setting mount and path via constructor. Implement IDisposable
1 parent 575c923 commit eeca03d

File tree

31 files changed

+266
-236
lines changed

31 files changed

+266
-236
lines changed

src/Directory.Build.props

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
<Copyright>Copyright © $(Company) $([System.DateTime]::Now.ToString(`yyyy`)). All rights reserved.</Copyright>
4141
<Authors>$(Company);Nikita Petko</Authors>
4242

43+
<VersionPrefix>1.0.9</VersionPrefix>
44+
4345
<RepositoryUrl>https://github.com/mfdlabs/grid-bot-libraries</RepositoryUrl>
4446
<RepositoryType>git</RepositoryType>
4547
</PropertyGroup>

src/clients/client-settings-client/ClientSettings.Client.csproj

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<Description>HTTP client used to interact with Roblox's Client Settings API Site.</Description>
4-
5-
<Version>1.0.4</Version>
64
</PropertyGroup>
75

86
<ItemGroup>

src/clients/thumbnails-client/Thumbnails.Client.csproj

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<Description>HTTP client used to interact with Roblox's Thumbnails API Site.</Description>
4-
5-
<Version>1.0.4</Version>
64
</PropertyGroup>
75

86
<ItemGroup>

src/clients/users-client/Users.Client.csproj

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<Description>HTTP client used to interact with Roblox's Users API Site.</Description>
4-
5-
<Version>1.0.4</Version>
64
</PropertyGroup>
75

86
<ItemGroup>

src/configuration/configuration/Configuration.csproj

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<Description>C# adaptation for @mfdlabs/environment.</Description>
4-
5-
<Version>1.0.8</Version>
64
</PropertyGroup>
75

86
<ItemGroup>

src/configuration/configuration/Implementation/EnvironmentProvider.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
namespace Configuration;
22

33
using System;
4-
using System.Linq;
5-
using System.Collections;
64

75
/// <summary>
86
/// Implementation for <see cref="BaseProvider"/> that uses Environment variables.

src/configuration/configuration/Implementation/VaultProvider.cs

Lines changed: 74 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515

1616
using Vault;
1717
using Logging;
18+
1819
using Threading.Extensions;
1920

21+
2022
/// <summary>
2123
/// Implementation for <see cref="BaseProvider"/> via Vault.
2224
/// </summary>
@@ -40,19 +42,25 @@ private static TimeSpan _defaultRefreshInterval
4042
nameof(Path),
4143
};
4244

43-
private static readonly ConcurrentBag<VaultProvider> _providers = new();
45+
private static readonly ConcurrentDictionary<string, VaultProvider> _providers = new();
4446

45-
private IDictionary<string, object> _cachedValues = new Dictionary<string, object>();
47+
private bool _disposed = false;
48+
49+
/// <summary>
50+
/// The raw cached values.
51+
/// </summary>
52+
protected IDictionary<string, object> _CachedValues = new Dictionary<string, object>();
4653

54+
private static readonly ManualResetEvent _refreshRequestEvent = new(false);
4755
private static readonly Thread _refreshThread;
4856
private static readonly IVaultClient _client = VaultClientFactory.Singleton.GetClient();
4957
private static readonly ILogger _staticLogger = Logger.Singleton;
5058

5159
/// <inheritdoc cref="IVaultProvider.Mount"/>
52-
public abstract string Mount { get; }
60+
public virtual string Mount { get; set; }
5361

5462
/// <inheritdoc cref="IVaultProvider.Path"/>
55-
public abstract string Path { get; }
63+
public virtual string Path { get; set; }
5664

5765
/// <summary>
5866
/// Gets the refresh interval for the settings provider.
@@ -78,16 +86,25 @@ static VaultProvider()
7886
_staticLogger?.Debug("VaultProvider: Started refresh thread!");
7987
}
8088

89+
/// <summary>
90+
/// Refresh all the currently registered providers immediately, this method is asynchronous
91+
/// and will return immediately.
92+
/// </summary>
93+
public static void RefreshAllProviders()
94+
{
95+
_refreshRequestEvent.Set();
96+
}
97+
8198
/// <inheritdoc cref="BaseProvider.SetRawValue{T}(string, T)"/>
8299
protected override void SetRawValue<T>(string variable, T value)
83100
{
84101
if (_client == null) return;
85102

86-
_logger?.Debug("VaultProvider: Set value in vault at path '{0}/{1}/{2}'", Mount, Path, variable);
103+
_logger?.Information("VaultProvider: Set value in vault at path '{0}/{1}/{2}'", Mount, Path, variable);
87104

88105
var realValue = ConvertFrom(value, typeof(T));
89106

90-
_cachedValues[variable] = realValue;
107+
_CachedValues[variable] = realValue;
91108

92109
ApplyCurrent();
93110
}
@@ -100,7 +117,7 @@ public void ApplyCurrent()
100117
// Build the current from the getters.
101118
var values = GetLatestValues();
102119

103-
_logger?.Debug("VaultProvider: Writing secret '{0}/{1}' to Vault!", Mount, Path);
120+
_logger?.Information("VaultProvider: Writing secret '{0}/{1}' to Vault!", Mount, Path);
104121

105122
_client?.V1.Secrets.KeyValue.V2.WriteSecretAsync(
106123
mountPoint: Mount,
@@ -128,7 +145,7 @@ private Dictionary<string, object> GetLatestValues()
128145

129146
try
130147
{
131-
_logger?.Debug("VaultProvider: Fetching initial value for {0}.{1}", GetType().Name, getterName);
148+
_logger?.Verbose("VaultProvider: Fetching initial value for {0}.{1}", GetType().Name, getterName);
132149

133150
var value = getter.GetGetMethod().Invoke(this, Array.Empty<object>());
134151
var realValue = value?.ToString() ?? string.Empty;
@@ -140,7 +157,7 @@ private Dictionary<string, object> GetLatestValues()
140157
}
141158
catch (TargetInvocationException ex)
142159
{
143-
_logger?.Debug("VaultProvider: Error occurred when fetching getter for '{0}.{1}': {2}", GetType().Name, getterName, ex.InnerException.Message);
160+
_logger?.Verbose("VaultProvider: Error occurred when fetching getter for '{0}.{1}': {2}", GetType().Name, getterName, ex.InnerException.Message);
144161

145162
newCachedValues.Add(getterName, string.Empty);
146163
}
@@ -153,45 +170,64 @@ private Dictionary<string, object> GetLatestValues()
153170
/// Construct a new instance of <see cref="VaultProvider"/>
154171
/// </summary>
155172
/// <param name="logger">The <see cref="ILogger"/></param>
156-
protected VaultProvider(ILogger logger = null)
173+
/// <param name="periodicRefresh">Should this periodically refresh?</param>
174+
protected VaultProvider(ILogger logger = null, bool periodicRefresh = true)
157175
{
158176
logger ??= Logger.Singleton;
159177

160178
SetLogger(logger);
161179

162-
_logger?.Debug("VaultProvider: Setup for '{0}/{1}' to refresh every '{2}' interval!", Mount, Path, RefreshInterval);
163-
164-
if (_providers.Contains(this))
180+
if (periodicRefresh)
165181
{
166-
_logger?.Debug("VaultProvider: Skipping setup for '{0}/{1}' because it is already setup!", Mount, Path);
182+
_logger?.Debug("VaultProvider: Setup for '{0}/{1}' to refresh every '{2}' interval!", Mount, Path, RefreshInterval);
167183

168-
return;
169-
}
184+
if (_providers.TryGetValue(GetType().ToString(), out _))
185+
{
186+
_logger?.Debug("VaultProvider: Skipping setup for '{0}/{1}' because it is already setup!", Mount, Path);
187+
188+
return;
189+
}
170190

171-
_providers.Add(this);
191+
_providers.TryAdd(GetType().ToString(), this);
192+
}
172193

173194
DoRefresh();
174195
}
175196

197+
/// <summary>
198+
/// Construct a new instance of <see cref="VaultProvider"/>
199+
/// </summary>
200+
/// <param name="mount">The <see cref="Mount"/></param>
201+
/// <param name="path">The <see cref="Path"/></param>
202+
/// <param name="logger">The <see cref="ILogger"/></param>
203+
/// <param name="periodicRefresh">Should this periodically refresh?</param>
204+
protected VaultProvider(string mount, string path = "", ILogger logger = null, bool periodicRefresh = true)
205+
: this(logger, periodicRefresh)
206+
{
207+
Mount = mount;
208+
Path = path;
209+
}
210+
176211
private static void RefreshThread()
177212
{
178213
while (true)
179214
{
180215
var providers = _providers.ToArray();
181216

182-
foreach (var provider in providers)
217+
foreach (var kvp in providers)
183218
{
184219
try
185220
{
186-
provider.DoRefresh();
221+
kvp.Value.DoRefresh();
187222
}
188223
catch (Exception ex)
189224
{
190225
_staticLogger?.Error(ex);
191226
}
192227
}
193228

194-
Thread.Sleep(RefreshInterval); // SetClient makes DoRefresh call.
229+
_refreshRequestEvent.WaitOne(RefreshInterval); // SetClient makes DoRefresh call.
230+
_refreshRequestEvent.Reset();
195231
}
196232
}
197233

@@ -211,8 +247,8 @@ private void DoRefresh()
211247
var values = secret.Data.Data;
212248
InvokePropertyChangedForChangedValues(values);
213249

214-
lock (_cachedValues)
215-
_cachedValues = values;
250+
lock (_CachedValues)
251+
_CachedValues = values;
216252
}
217253
catch (VaultApiException ex)
218254
{
@@ -245,7 +281,6 @@ private bool HasProperty(ref string name)
245281
if (property == null)
246282
{
247283
_logger?.Debug("VaultProvider: Skipping property changed handler for '{0}' because settings provider '{1}' does not define it!", name, this);
248-
_logger?.Warning("{0}: Unknown property '{1}', make sure it is defined in the settings provider or has a appropriate [{2}] attribute!", GetType().Name, name, nameof(SettingNameAttribute));
249284

250285
return false;
251286
}
@@ -258,7 +293,7 @@ private bool HasProperty(ref string name)
258293

259294
private void InvokePropertyChangedForChangedValues(IDictionary<string, object> newValues)
260295
{
261-
if (_cachedValues.Count == 0)
296+
if (_CachedValues.Count == 0)
262297
{
263298
foreach (var kvp in newValues)
264299
{
@@ -272,7 +307,7 @@ private void InvokePropertyChangedForChangedValues(IDictionary<string, object> n
272307
var propertyName = kvp.Key;
273308
if (!HasProperty(ref propertyName)) continue;
274309

275-
_logger?.Debug("VaultProvider: Invoking property changed handler for '{0}'", propertyName);
310+
_logger?.Verbose("VaultProvider: Invoking property changed handler for '{0}'", propertyName);
276311

277312
PropertyChanged?.Invoke(this, new(propertyName));
278313
}
@@ -282,7 +317,7 @@ private void InvokePropertyChangedForChangedValues(IDictionary<string, object> n
282317

283318
foreach (var kvp in newValues)
284319
{
285-
if (_cachedValues.TryGetValue(kvp.Key, out var value))
320+
if (_CachedValues.TryGetValue(kvp.Key, out var value))
286321
if (value.ToString().Equals(kvp.Value.ToString())) continue;
287322

288323
if (_propertyNamesIgnored.Contains(kvp.Key))
@@ -309,8 +344,8 @@ protected override bool GetRawValue(string key, out string value)
309344
{
310345
object v;
311346

312-
lock (_cachedValues)
313-
if (!_cachedValues.TryGetValue(key, out v))
347+
lock (_CachedValues)
348+
if (!_CachedValues.TryGetValue(key, out v))
314349
return base.GetRawValue(key, out value);
315350

316351
if (v is JsonElement element)
@@ -320,4 +355,15 @@ protected override bool GetRawValue(string key, out string value)
320355

321356
return true;
322357
}
358+
359+
/// <inheritdoc cref="IDisposable.Dispose"/>
360+
public void Dispose()
361+
{
362+
if (_disposed) return;
363+
364+
GC.SuppressFinalize(this);
365+
366+
_providers.TryRemove(GetType().ToString(), out _);
367+
_disposed = true;
368+
}
323369
}

src/configuration/configuration/Interfaces/IVaultProvider.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
namespace Configuration;
22

3-
using VaultSharp;
3+
using System;
44

55
/// <summary>
66
/// Represents a <see cref="IConfigurationProvider"/> backed by Vault.
77
/// </summary>
8-
public interface IVaultProvider : IConfigurationProvider
8+
public interface IVaultProvider : IConfigurationProvider, IDisposable
99
{
1010
/// <summary>
1111
/// Gets the mount path.

src/configuration/core/Configuration.Core.csproj

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<Description>Helpers for configuration in applications.</Description>
4-
5-
<Version>1.0.5</Version>
64
</PropertyGroup>
75

86
<ItemGroup>

src/file-system/file-system/FileSystem.csproj

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<Description>File system helpers and more!</Description>
4-
5-
<Version>1.0.5</Version>
64
</PropertyGroup>
75

86
<ItemGroup>

0 commit comments

Comments
 (0)