Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Commit b276534

Browse files
authored
Merge pull request #1674 from github/fixes/1670-avoid-multiple-writes
Avoid overlapping writes to metrics.json
2 parents 12a81c6 + f8208a8 commit b276534

File tree

1 file changed

+22
-7
lines changed

1 file changed

+22
-7
lines changed

src/GitHub.VisualStudio/Services/UsageService.cs

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System;
22
using System.Collections.Generic;
33
using System.ComponentModel.Composition;
4-
using System.Globalization;
54
using System.IO;
65
using System.Text;
76
using System.Threading;
@@ -17,14 +16,15 @@
1716
namespace GitHub.Services
1817
{
1918
[Export(typeof(IUsageService))]
20-
public class UsageService : IUsageService
19+
public sealed class UsageService : IUsageService, IDisposable
2120
{
2221
const string StoreFileName = "metrics.json";
2322
const string UserStoreFileName = "user.json";
2423
static readonly ILogger log = LogManager.ForContext<UsageService>();
2524

2625
readonly IGitHubServiceProvider serviceProvider;
2726
readonly IEnvironment environment;
27+
readonly SemaphoreSlim writeSemaphoreSlim = new SemaphoreSlim(1, 1);
2828

2929
string storePath;
3030
string userStorePath;
@@ -37,6 +37,11 @@ public UsageService(IGitHubServiceProvider serviceProvider, IEnvironment environ
3737
this.environment = environment;
3838
}
3939

40+
public void Dispose()
41+
{
42+
writeSemaphoreSlim.Dispose();
43+
}
44+
4045
public async Task<Guid> GetUserGuid()
4146
{
4247
await Initialize();
@@ -102,7 +107,7 @@ public async Task<UsageData> ReadLocalData()
102107
SimpleJson.DeserializeObject<UsageData>(json) :
103108
new UsageData { Reports = new List<UsageModel>() };
104109
}
105-
catch(Exception ex)
110+
catch (Exception ex)
106111
{
107112
log.Error(ex, "Error deserializing usage");
108113
return new UsageData { Reports = new List<UsageModel>() };
@@ -115,11 +120,12 @@ public async Task WriteLocalData(UsageData data)
115120
{
116121
Directory.CreateDirectory(Path.GetDirectoryName(storePath));
117122
var json = SimpleJson.SerializeObject(data);
123+
118124
await WriteAllTextAsync(storePath, json);
119125
}
120126
catch (Exception ex)
121127
{
122-
log.Error(ex,"Failed to write usage data");
128+
log.Error(ex, "Failed to write usage data");
123129
}
124130
}
125131

@@ -149,10 +155,19 @@ async Task<string> ReadAllTextAsync(string path)
149155

150156
async Task WriteAllTextAsync(string path, string text)
151157
{
152-
using (var s = new FileStream(path, FileMode.Create))
153-
using (var w = new StreamWriter(s, Encoding.UTF8))
158+
// Avoid IOException when metrics updated multiple times in quick succession
159+
await writeSemaphoreSlim.WaitAsync();
160+
try
161+
{
162+
using (var s = new FileStream(path, FileMode.Create))
163+
using (var w = new StreamWriter(s, Encoding.UTF8))
164+
{
165+
await w.WriteAsync(text);
166+
}
167+
}
168+
finally
154169
{
155-
await w.WriteAsync(text);
170+
writeSemaphoreSlim.Release();
156171
}
157172
}
158173

0 commit comments

Comments
 (0)