Skip to content

Commit

Permalink
Sync read the file in the data source's constructor, fix GH actions t…
Browse files Browse the repository at this point in the history
…rigger
  • Loading branch information
z4kn4fein committed Apr 5, 2022
1 parent ac657c8 commit 767962f
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 65 deletions.
1 change: 1 addition & 0 deletions .github/workflows/linux-macOS-CI.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: Build on Linux and macOS
on:
push:
branches: [ master ]
paths-ignore:
- '**.md'
- 'appveyor*'
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/sonar-analysis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: SonarCloud Analysis
on:
push:
branches: [ master ]
paths-ignore:
- '**.md'
- 'appveyor*'
Expand Down
4 changes: 2 additions & 2 deletions src/ConfigCat.Client.Tests/OverrideTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ public async Task LocalFile_Watcher_Reload()
Assert.AreEqual("initial", await client.GetValueAsync("fakeKey", string.Empty));

await WriteContent(SampleFileToCreate, "modified");
await Task.Delay(1500);
await Task.Delay(2000);

Assert.AreEqual("modified", await client.GetValueAsync("fakeKey", string.Empty));

Expand All @@ -384,7 +384,7 @@ public async Task LocalFile_Watcher_Reload_Sync()
Assert.AreEqual("initial", client.GetValue("fakeKey", string.Empty));

await WriteContent(SampleFileToCreate, "modified");
await Task.Delay(1500);
await Task.Delay(2000);

Assert.AreEqual("modified", client.GetValue("fakeKey", string.Empty));

Expand Down
94 changes: 31 additions & 63 deletions src/ConfigCatClient/Override/LocalFileDataSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ internal sealed class LocalFileDataSource : IOverrideDataSource
private readonly ILogger logger;
private readonly TaskCompletionSource<bool> asyncInit = new();
private readonly ManualResetEvent syncInit = new(false);
private readonly CancellationTokenSource pollerCancellationTokenSource = new();
private readonly CancellationTokenSource cancellationTokenSource = new();

private volatile IDictionary<string, Setting> overrideValues;

Expand All @@ -28,90 +28,66 @@ public LocalFileDataSource(string filePath, bool autoReload, ILogger logger)
if (!File.Exists(filePath))
{
logger.Error($"File {filePath} does not exist.");
this.SetInitialized();
return;
}

this.fullPath = Path.GetFullPath(filePath);
this.logger = logger;

this.StartFileReading(autoReload);
}
this.ReloadFile();

public IDictionary<string, Setting> GetOverrides()
{
if (this.overrideValues != null) return this.overrideValues;
this.syncInit.WaitOne();
return this.overrideValues ?? new Dictionary<string, Setting>();
if (autoReload)
{
this.StartWatch();
}
}

public async Task<IDictionary<string, Setting>> GetOverridesAsync()
{
if (this.overrideValues != null) return this.overrideValues;
await this.asyncInit.Task.ConfigureAwait(false);
return this.overrideValues ?? new Dictionary<string, Setting>();
}
public IDictionary<string, Setting> GetOverrides() => this.overrideValues ?? new Dictionary<string, Setting>();

private void StartFileReading(bool autoReload)
{
Task.Run(async () =>
{
try
{
await ReadFileAsync();
public Task<IDictionary<string, Setting>> GetOverridesAsync() => Task.FromResult(this.overrideValues ?? new Dictionary<string, Setting>());

if (autoReload)
{
await this.StartWatchAsync();
}
}
finally
{
this.SetInitialized();
}
});
}

private async Task StartWatchAsync()
private void StartWatch()
{
this.logger.Information($"Watching {this.fullPath} for changes.");
while (!this.pollerCancellationTokenSource.IsCancellationRequested)
Task.Run(async () =>
{
try
this.logger.Information($"Watching {this.fullPath} for changes.");
while (!this.cancellationTokenSource.IsCancellationRequested)
{
try
{
var lastWriteTime = File.GetLastWriteTimeUtc(this.fullPath);
if (lastWriteTime > this.fileLastWriteTime)
try
{
this.logger.Information($"Reload file {this.fullPath}.");
await ReadFileAsync();
var lastWriteTime = File.GetLastWriteTimeUtc(this.fullPath);
if (lastWriteTime > this.fileLastWriteTime)
{
this.logger.Information($"Reload file {this.fullPath}.");
this.ReloadFile();
}
}
}

finally
finally
{
await Task.Delay(FILE_POLL_INTERVAL, this.cancellationTokenSource.Token);
}
}
catch (OperationCanceledException)
{
await Task.Delay(FILE_POLL_INTERVAL, this.pollerCancellationTokenSource.Token);
// ignore exceptions from cancellation.
}
}
catch (OperationCanceledException)
{
// ignore exceptions from cancellation.
}
}
});
}

private async Task ReadFileAsync()
private void ReloadFile()
{
try
{
for (int i = 1; i <= MAX_WAIT_ITERATIONS; i++)
{
try
{
using var stream = new FileStream(this.fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using var reader = new StreamReader(stream);
var content = await reader.ReadToEndAsync();
var content = File.ReadAllText(this.fullPath);
var simplified = content.DeserializeOrDefault<SimplifiedConfig>();
if (simplified?.Entries != null)
{
Expand All @@ -130,7 +106,7 @@ private async Task ReadFileAsync()
if (i >= MAX_WAIT_ITERATIONS)
throw;

await Task.Delay(WAIT_TIME_FOR_UNLOCK);
Thread.Sleep(WAIT_TIME_FOR_UNLOCK);
}
}
}
Expand All @@ -141,20 +117,12 @@ private async Task ReadFileAsync()
finally
{
this.fileLastWriteTime = File.GetLastWriteTimeUtc(this.fullPath);
this.SetInitialized();
}
}

private void SetInitialized()
{
this.syncInit.Set();
this.asyncInit.TrySetResult(true);
}

public void Dispose()
{
this.pollerCancellationTokenSource.Cancel();
this.syncInit.Dispose();
this.cancellationTokenSource.Cancel();
}

private sealed class SimplifiedConfig
Expand Down

0 comments on commit 767962f

Please sign in to comment.