perf: add buffering to `FileWritingService` (#435)
This commit is contained in:
Родитель
6ad4dfc89a
Коммит
9df530aa70
|
@ -1,14 +1,17 @@
|
|||
namespace Microsoft.ComponentDetection.Common;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.ComponentDetection.Common.Exceptions;
|
||||
|
||||
public class FileWritingService : IFileWritingService
|
||||
public sealed class FileWritingService : IFileWritingService
|
||||
{
|
||||
public const string TimestampFormatString = "yyyyMMddHHmmssfff";
|
||||
|
||||
private readonly object lockObject = new object();
|
||||
private readonly string timestamp = DateTime.Now.ToString(TimestampFormatString);
|
||||
private readonly ConcurrentDictionary<string, StreamWriter> bufferedStreams = new();
|
||||
|
||||
public string BasePath { get; private set; }
|
||||
|
||||
|
@ -26,10 +29,13 @@ public class FileWritingService : IFileWritingService
|
|||
{
|
||||
relativeFilePath = this.ResolveFilePath(relativeFilePath);
|
||||
|
||||
lock (this.lockObject)
|
||||
if (!this.bufferedStreams.TryGetValue(relativeFilePath, out var streamWriter))
|
||||
{
|
||||
File.AppendAllText(relativeFilePath, text);
|
||||
streamWriter = new StreamWriter(relativeFilePath, true);
|
||||
this.bufferedStreams.TryAdd(relativeFilePath, streamWriter);
|
||||
}
|
||||
|
||||
streamWriter.Write(text);
|
||||
}
|
||||
|
||||
public void WriteFile(string relativeFilePath, string text)
|
||||
|
@ -66,4 +72,33 @@ public class FileWritingService : IFileWritingService
|
|||
throw new InvalidOperationException("Base path has not yet been initialized in File Writing Service!");
|
||||
}
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (filename, streamWriter) in this.bufferedStreams)
|
||||
{
|
||||
streamWriter.Dispose();
|
||||
this.bufferedStreams.TryRemove(filename, out _);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
foreach (var (filename, streamWriter) in this.bufferedStreams)
|
||||
{
|
||||
await streamWriter.DisposeAsync();
|
||||
this.bufferedStreams.TryRemove(filename, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
namespace Microsoft.ComponentDetection.Common;
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
// All file paths are relative and will replace occurrences of {timestamp} with the shared file timestamp.
|
||||
public interface IFileWritingService
|
||||
public interface IFileWritingService : IDisposable, IAsyncDisposable
|
||||
{
|
||||
void Init(string basePath);
|
||||
|
||||
|
|
|
@ -32,6 +32,9 @@ try
|
|||
|
||||
Console.WriteLine($"Execution finished, status: {exitCode}.");
|
||||
|
||||
// Manually dispose to flush logs as we force exit
|
||||
await serviceProvider.DisposeAsync();
|
||||
|
||||
// force an exit, not letting any lingering threads not responding.
|
||||
Environment.Exit(exitCode);
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ public class FileWritingServiceTests
|
|||
var fileLocation = Path.Combine(this.tempFolder, relativeDir);
|
||||
File.Create(fileLocation).Dispose();
|
||||
this.serviceUnderTest.AppendToFile(relativeDir, "someSampleText");
|
||||
this.serviceUnderTest.Dispose();
|
||||
var text = File.ReadAllText(Path.Combine(this.tempFolder, relativeDir));
|
||||
text
|
||||
.Should().Be("someSampleText");
|
||||
|
@ -62,6 +63,7 @@ public class FileWritingServiceTests
|
|||
var relativeDir = "somefile_{timestamp}.txt";
|
||||
this.serviceUnderTest.WriteFile(relativeDir, "sampleText");
|
||||
this.serviceUnderTest.AppendToFile(relativeDir, "sampleText2");
|
||||
this.serviceUnderTest.Dispose();
|
||||
var files = Directory.GetFiles(this.tempFolder);
|
||||
files
|
||||
.Should().NotBeEmpty();
|
||||
|
|
Загрузка…
Ссылка в новой задаче