diff --git a/src/Microsoft.Tye.Hosting/DockerRunner.cs b/src/Microsoft.Tye.Hosting/DockerRunner.cs index 993c5b45..f5124c17 100644 --- a/src/Microsoft.Tye.Hosting/DockerRunner.cs +++ b/src/Microsoft.Tye.Hosting/DockerRunner.cs @@ -15,7 +15,7 @@ namespace Microsoft.Tye.Hosting { public class DockerRunner : IApplicationProcessor { - private const string DOCKER_REPLICA_STORE = "docker"; + private const string DockerReplicaStore = "docker"; private static readonly TimeSpan DockerStopTimeout = TimeSpan.FromSeconds(30); private readonly ILogger _logger; @@ -301,7 +301,7 @@ namespace Microsoft.Tye.Hosting private async Task PurgeFromPreviousRun() { - var dockerReplicas = await _replicaRegistry.GetEvents(DOCKER_REPLICA_STORE); + var dockerReplicas = await _replicaRegistry.GetEvents(DockerReplicaStore); foreach (var replica in dockerReplicas) { var container = replica["container"]; @@ -309,12 +309,12 @@ namespace Microsoft.Tye.Hosting _logger.LogInformation("removed contaienr {container} from previous run", container); } - _replicaRegistry.DeleteStore(DOCKER_REPLICA_STORE); + _replicaRegistry.DeleteStore(DockerReplicaStore); } private void WriteReplicaToStore(string container) { - _replicaRegistry.WriteReplicaEvent(DOCKER_REPLICA_STORE, new Dictionary() + _replicaRegistry.WriteReplicaEvent(DockerReplicaStore, new Dictionary() { ["container"] = container }); diff --git a/src/Microsoft.Tye.Hosting/ProcessRunner.cs b/src/Microsoft.Tye.Hosting/ProcessRunner.cs index 3a5e7176..6ad4accb 100644 --- a/src/Microsoft.Tye.Hosting/ProcessRunner.cs +++ b/src/Microsoft.Tye.Hosting/ProcessRunner.cs @@ -17,7 +17,7 @@ namespace Microsoft.Tye.Hosting { public class ProcessRunner : IApplicationProcessor { - private const string PROCESS_REPLICA_STORE = "process"; + private const string ProcessReplicaStore = "process"; private readonly ILogger _logger; private readonly ProcessRunnerOptions _options; @@ -309,7 +309,7 @@ namespace Microsoft.Tye.Hosting private async Task PurgeFromPreviousRun() { - var processReplicas = await _replicaRegistry.GetEvents(PROCESS_REPLICA_STORE); + var processReplicas = await _replicaRegistry.GetEvents(ProcessReplicaStore); foreach (var replica in processReplicas) { if (int.TryParse(replica["pid"], out var pid)) @@ -319,12 +319,12 @@ namespace Microsoft.Tye.Hosting } } - _replicaRegistry.DeleteStore(PROCESS_REPLICA_STORE); + _replicaRegistry.DeleteStore(ProcessReplicaStore); } private void WriteReplicaToStore(string pid) { - _replicaRegistry.WriteReplicaEvent(PROCESS_REPLICA_STORE, new Dictionary() + _replicaRegistry.WriteReplicaEvent(ProcessReplicaStore, new Dictionary() { ["pid"] = pid }); diff --git a/src/Microsoft.Tye.Hosting/ReplicaRegistry.cs b/src/Microsoft.Tye.Hosting/ReplicaRegistry.cs index 43a307d8..0cf6a615 100644 --- a/src/Microsoft.Tye.Hosting/ReplicaRegistry.cs +++ b/src/Microsoft.Tye.Hosting/ReplicaRegistry.cs @@ -3,56 +3,52 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.Json; using System.Threading.Tasks; -using Microsoft.Tye.Hosting.Model; using Microsoft.Extensions.Logging; -using System.Threading; -using System.Collections.Concurrent; namespace Microsoft.Tye.Hosting { public class ReplicaRegistry : IDisposable { private readonly ILogger _logger; - private readonly ConcurrentDictionary _fileWriteSemaphores; + private readonly ConcurrentDictionary _fileWriteLocks; private readonly string _tyeFolderPath; - public ReplicaRegistry(Model.Application application, ILogger logger) + public ReplicaRegistry(string directory, ILogger logger) { _logger = logger; - _fileWriteSemaphores = new ConcurrentDictionary(); - _tyeFolderPath = Path.Join(Path.GetDirectoryName(application.Source), ".tye"); + _fileWriteLocks = new ConcurrentDictionary(); + _tyeFolderPath = Path.Join(directory, ".tye"); + } + public bool WriteReplicaEvent(string storeName, IDictionary replicaRecord) + { if (!Directory.Exists(_tyeFolderPath)) { Directory.CreateDirectory(_tyeFolderPath); } - } - public bool WriteReplicaEvent(string storeName, IDictionary replicaRecord) - { var filePath = Path.Join(_tyeFolderPath, GetStoreFile(storeName)); var contents = JsonSerializer.Serialize(replicaRecord, new JsonSerializerOptions { WriteIndented = false }); - var semaphore = GetSempahoreForStore(storeName); + var lockObj = GetLockForStore(storeName); - semaphore.Wait(); - try + lock (lockObj) { - File.AppendAllText(filePath, contents + Environment.NewLine); - return true; - } - catch (DirectoryNotFoundException ex) - { - _logger.LogWarning(ex, "tye folder is not found. file: {file}", filePath); - return false; - } - finally - { - semaphore.Release(); + try + { + File.AppendAllText(filePath, contents + Environment.NewLine); + return true; + } + catch (DirectoryNotFoundException ex) + { + _logger.LogWarning(ex, "tye folder is not found. file: {file}", filePath); + return false; + } } } @@ -75,7 +71,7 @@ namespace Microsoft.Tye.Hosting public async ValueTask>> GetEvents(string storeName) { var filePath = Path.Join(_tyeFolderPath, GetStoreFile(storeName)); - + if (!File.Exists(filePath)) { return Array.Empty>(); @@ -89,9 +85,9 @@ namespace Microsoft.Tye.Hosting .ToList(); } - private SemaphoreSlim GetSempahoreForStore(string storeName) + private object GetLockForStore(string storeName) { - return _fileWriteSemaphores.GetOrAdd(storeName, _ => new SemaphoreSlim(1, 1)); + return _fileWriteLocks.GetOrAdd(storeName, _ => new object()); } private string GetStoreFile(string storeName) => $"{storeName}_store"; diff --git a/src/Microsoft.Tye.Hosting/TyeHost.cs b/src/Microsoft.Tye.Hosting/TyeHost.cs index 87afccba..dcf4886b 100644 --- a/src/Microsoft.Tye.Hosting/TyeHost.cs +++ b/src/Microsoft.Tye.Hosting/TyeHost.cs @@ -83,7 +83,7 @@ namespace Microsoft.Tye.Hosting var configuration = app.Configuration; - _replicaRegistry = new ReplicaRegistry(_application, _logger); + _replicaRegistry = new ReplicaRegistry(_application.ContextDirectory, _logger); _processor = CreateApplicationProcessor(_replicaRegistry, _args, _servicesToDebug, _logger, configuration); diff --git a/test/E2ETest/TestHelpers.cs b/test/E2ETest/TestHelpers.cs index 4bcd19d2..41f6dd93 100644 --- a/test/E2ETest/TestHelpers.cs +++ b/test/E2ETest/TestHelpers.cs @@ -128,7 +128,7 @@ namespace E2ETest static async Task Purge(TyeHost host) { var logger = host.DashboardWebApplication!.Logger; - var replicaRegistry = new ReplicaRegistry(host.Application, logger); + var replicaRegistry = new ReplicaRegistry(host.Application.ContextDirectory, logger); var processRunner = new ProcessRunner(logger, replicaRegistry, new ProcessRunnerOptions()); var dockerRunner = new DockerRunner(logger, replicaRegistry); diff --git a/test/E2ETest/TestOutputLogEventSink.cs b/test/E2ETest/TestOutputLogEventSink.cs index d42f2c67..e3cbe39e 100644 --- a/test/E2ETest/TestOutputLogEventSink.cs +++ b/test/E2ETest/TestOutputLogEventSink.cs @@ -8,11 +8,11 @@ namespace E2ETest { public class TestOutputLogEventSink : ILogEventSink, IConsole, IStandardStreamWriter { - private readonly ITestOutputHelper output; + private readonly ITestOutputHelper _output; public TestOutputLogEventSink(ITestOutputHelper output) { - this.output = output; + _output = output; } public IStandardStreamWriter Out => this; @@ -27,13 +27,13 @@ namespace E2ETest public void Emit(LogEvent logEvent) { - output.WriteLine(logEvent.RenderMessage()); + _output.WriteLine(logEvent.RenderMessage()); } public void Write(string value) { // our usage of IConsole includes newlines, so strip them out. - output.WriteLine(value.TrimEnd('\r', '\n')); + _output.WriteLine(value.TrimEnd('\r', '\n')); } } } diff --git a/test/E2ETest/TyePurgeTests.cs b/test/E2ETest/TyePurgeTests.cs index d7e883a4..c186871f 100644 --- a/test/E2ETest/TyePurgeTests.cs +++ b/test/E2ETest/TyePurgeTests.cs @@ -21,13 +21,13 @@ namespace E2ETest { public class TyePurgeTests { - private readonly ITestOutputHelper output; - private readonly TestOutputLogEventSink sink; + private readonly ITestOutputHelper _output; + private readonly TestOutputLogEventSink _sink; public TyePurgeTests(ITestOutputHelper output) { - this.output = output; - sink = new TestOutputLogEventSink(output); + _output = output; + _sink = new TestOutputLogEventSink(output); } [Fact] @@ -39,11 +39,11 @@ namespace E2ETest var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "tye.yaml")); var tyeDir = new DirectoryInfo(Path.Combine(tempDirectory.DirectoryPath, ".tye")); - var outputContext = new OutputContext(sink, Verbosity.Debug); + var outputContext = new OutputContext(_sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); var host = new TyeHost(application.ToHostingApplication(), Array.Empty()) { - Sink = sink, + Sink = _sink, }; try @@ -84,11 +84,11 @@ namespace E2ETest var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "tye.yaml")); var tyeDir = new DirectoryInfo(Path.Combine(tempDirectory.DirectoryPath, ".tye")); - var outputContext = new OutputContext(sink, Verbosity.Debug); + var outputContext = new OutputContext(_sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); var host = new TyeHost(application.ToHostingApplication(), Array.Empty()) { - Sink = sink, + Sink = _sink, }; try @@ -101,7 +101,7 @@ namespace E2ETest Assert.True(Directory.Exists(tyeDir.FullName)); Assert.Subset(new HashSet(GetAllPids()), new HashSet(pids)); - Assert.Subset(new HashSet(await DockerAssert.GetRunningContainersIdsAsync(output)), + Assert.Subset(new HashSet(await DockerAssert.GetRunningContainersIdsAsync(_output)), new HashSet(containers)); await TestHelpers.PurgeHostAndWaitForGivenReplicasToStop(host, @@ -110,7 +110,7 @@ namespace E2ETest var runningPids = new HashSet(GetAllPids()); Assert.True(pids.All(pid => !runningPids.Contains(pid))); var runningContainers = - new HashSet(await DockerAssert.GetRunningContainersIdsAsync(output)); + new HashSet(await DockerAssert.GetRunningContainersIdsAsync(_output)); Assert.True(containers.All(c => !runningContainers.Contains(c))); } finally diff --git a/test/E2ETest/TyeRunTests.cs b/test/E2ETest/TyeRunTests.cs index 2d5df720..f2eeb912 100644 --- a/test/E2ETest/TyeRunTests.cs +++ b/test/E2ETest/TyeRunTests.cs @@ -22,14 +22,14 @@ namespace E2ETest { public class TyeRunTests { - private readonly ITestOutputHelper output; - private readonly TestOutputLogEventSink sink; + private readonly ITestOutputHelper _output; + private readonly TestOutputLogEventSink _sink; private readonly JsonSerializerOptions _options; public TyeRunTests(ITestOutputHelper output) { - this.output = output; - sink = new TestOutputLogEventSink(output); + _output = output; + _sink = new TestOutputLogEventSink(output); _options = new JsonSerializerOptions() { @@ -49,11 +49,11 @@ namespace E2ETest DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath); var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "test-project.csproj")); - var outputContext = new OutputContext(sink, Verbosity.Debug); + var outputContext = new OutputContext(_sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); using var host = new TyeHost(application.ToHostingApplication(), Array.Empty()) { - Sink = sink, + Sink = _sink, }; await host.StartAsync(); @@ -91,8 +91,8 @@ namespace E2ETest var response = await client.SendAsync(request); var text = await response.Content.ReadAsStringAsync(); - output.WriteLine($"Logs for service: {service.Description.Name}"); - output.WriteLine(text); + _output.WriteLine($"Logs for service: {service.Description.Name}"); + _output.WriteLine(text); } } finally @@ -109,11 +109,11 @@ namespace E2ETest DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath); var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "tye.yaml")); - var outputContext = new OutputContext(sink, Verbosity.Debug); + var outputContext = new OutputContext(_sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); using var host = new TyeHost(application.ToHostingApplication(), Array.Empty()) { - Sink = sink, + Sink = _sink, }; await host.StartAsync(); @@ -146,11 +146,11 @@ namespace E2ETest DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath); var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "tye.yaml")); - var outputContext = new OutputContext(sink, Verbosity.Debug); + var outputContext = new OutputContext(_sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); using var host = new TyeHost(application.ToHostingApplication(), Array.Empty()) { - Sink = sink, + Sink = _sink, }; var handler = new HttpClientHandler @@ -198,8 +198,8 @@ namespace E2ETest var response = await client.SendAsync(request); var text = await response.Content.ReadAsStringAsync(); - output.WriteLine($"Logs for service: {s.Description.Name}"); - output.WriteLine(text); + _output.WriteLine($"Logs for service: {s.Description.Name}"); + _output.WriteLine(text); } await host.StopAsync(); @@ -216,11 +216,11 @@ namespace E2ETest DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath); var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "tye.yaml")); - var outputContext = new OutputContext(sink, Verbosity.Debug); + var outputContext = new OutputContext(_sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); using var host = new TyeHost(application.ToHostingApplication(), new[] { "--docker" }) { - Sink = sink, + Sink = _sink, }; await host.StartAsync(); @@ -258,11 +258,11 @@ namespace E2ETest var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "test-project.csproj")); // Debug targets can be null if not specified, so make sure calling host.Start does not throw. - var outputContext = new OutputContext(sink, Verbosity.Debug); + var outputContext = new OutputContext(_sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); using var host = new TyeHost(application.ToHostingApplication(), Array.Empty()) { - Sink = sink, + Sink = _sink, }; await host.StartAsync(); @@ -299,7 +299,7 @@ namespace E2ETest var appResponse = await client.GetAsync(uriBackendProcess); var content = await appResponse.Content.ReadAsStringAsync(); - output.WriteLine(content); + _output.WriteLine(content); Assert.Equal(HttpStatusCode.OK, appResponse.StatusCode); if (serviceName == "frontend") { @@ -317,8 +317,8 @@ namespace E2ETest var response = await client.SendAsync(request); var text = await response.Content.ReadAsStringAsync(); - output.WriteLine($"Logs for service: {s.Description.Name}"); - output.WriteLine(text); + _output.WriteLine($"Logs for service: {s.Description.Name}"); + _output.WriteLine(text); } } }