Retry when lock file is occupied by other process
This commit is contained in:
parent
0dad10253b
commit
a50a5a9d73
4 changed files with 120 additions and 13 deletions
|
@ -17,7 +17,7 @@ namespace Microsoft.DotNet.ProjectModel.Graph
|
||||||
{
|
{
|
||||||
public static LockFile Read(string lockFilePath)
|
public static LockFile Read(string lockFilePath)
|
||||||
{
|
{
|
||||||
using (var stream = new FileStream(lockFilePath, FileMode.Open, FileAccess.Read, FileShare.Read))
|
using (var stream = ResilientFileStreamOpener.OpenFile(lockFilePath))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -34,7 +34,7 @@ namespace Microsoft.DotNet.ProjectModel.Graph
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static LockFile Read(string lockFilePath, Stream stream)
|
public static LockFile Read(string lockFilePath, Stream stream)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.ProjectModel.Utilities
|
||||||
|
{
|
||||||
|
public class ResilientFileStreamOpener
|
||||||
|
{
|
||||||
|
public static FileStream OpenFile(string filepath)
|
||||||
|
{
|
||||||
|
return OpenFile(filepath, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FileStream OpenFile(string filepath, int retry)
|
||||||
|
{
|
||||||
|
if (retry < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Retry can't be fewer than 0", nameof(retry));
|
||||||
|
}
|
||||||
|
|
||||||
|
var count = 0;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
if (++count > retry)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Thread.Sleep(500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.DotNet.ProjectModel.Graph;
|
using Microsoft.DotNet.ProjectModel.Graph;
|
||||||
|
using Microsoft.DotNet.ProjectModel.Utilities;
|
||||||
|
|
||||||
namespace Microsoft.DotNet.ProjectModel
|
namespace Microsoft.DotNet.ProjectModel
|
||||||
{
|
{
|
||||||
|
@ -205,9 +206,24 @@ namespace Microsoft.DotNet.ProjectModel
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
currentEntry.FilePath = Path.Combine(projectDirectory, LockFile.FileName);
|
currentEntry.FilePath = Path.Combine(projectDirectory, LockFile.FileName);
|
||||||
currentEntry.Model = LockFileReader.Read(currentEntry.FilePath);
|
|
||||||
|
using (var fs = ResilientFileStreamOpener.OpenFile(currentEntry.FilePath, retry: 2))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
currentEntry.Model = LockFileReader.Read(currentEntry.FilePath, fs);
|
||||||
currentEntry.UpdateLastWriteTimeUtc();
|
currentEntry.UpdateLastWriteTimeUtc();
|
||||||
}
|
}
|
||||||
|
catch (FileFormatException ex)
|
||||||
|
{
|
||||||
|
throw ex.WithFilePath(currentEntry.FilePath);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw FileFormatException.Create(ex, currentEntry.FilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return currentEntry;
|
return currentEntry;
|
||||||
|
|
|
@ -4,6 +4,9 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.DotNet.ProjectModel.Graph;
|
||||||
using Microsoft.DotNet.TestFramework;
|
using Microsoft.DotNet.TestFramework;
|
||||||
using Microsoft.DotNet.Tools.Test.Utilities;
|
using Microsoft.DotNet.Tools.Test.Utilities;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
@ -32,7 +35,7 @@ namespace Microsoft.DotNet.ProjectModel.Server.Tests
|
||||||
{
|
{
|
||||||
_loggerFactory.AddConsole(LogLevel.Information);
|
_loggerFactory.AddConsole(LogLevel.Information);
|
||||||
}
|
}
|
||||||
else
|
else if (testVerbose == "0")
|
||||||
{
|
{
|
||||||
_loggerFactory.AddConsole(LogLevel.Warning);
|
_loggerFactory.AddConsole(LogLevel.Warning);
|
||||||
}
|
}
|
||||||
|
@ -362,5 +365,48 @@ namespace Microsoft.DotNet.ProjectModel.Server.Tests
|
||||||
clearError.Payload.AsJObject().AssertProperty("Message", null as string);
|
clearError.Payload.AsJObject().AssertProperty("Message", null as string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(500, true)]
|
||||||
|
[InlineData(3000, false)]
|
||||||
|
public void WaitForLockFileReleased(int occupyFileFor, bool expectSuccess)
|
||||||
|
{
|
||||||
|
var testProject = _testAssetsManager.CreateTestInstance("EmptyConsoleApp")
|
||||||
|
.WithLockFiles()
|
||||||
|
.TestRoot;
|
||||||
|
|
||||||
|
using (var server = new DthTestServer(_loggerFactory))
|
||||||
|
using (var client = new DthTestClient(server))
|
||||||
|
{
|
||||||
|
var lockFilePath = Path.Combine(testProject, LockFile.FileName);
|
||||||
|
var lockFileContent = File.ReadAllText(lockFilePath);
|
||||||
|
var fs = new FileStream(lockFilePath, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||||
|
|
||||||
|
// Test the platform
|
||||||
|
// A sharing violation is expected in following code. Otherwise the FileSteam is not implemented correctly.
|
||||||
|
Assert.ThrowsAny<IOException>(() =>
|
||||||
|
{
|
||||||
|
new FileStream(lockFilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
|
});
|
||||||
|
|
||||||
|
var task = Task.Run(() =>
|
||||||
|
{
|
||||||
|
// WorkspaceContext will try to open the lock file for 3 times with 500 ms interval in between.
|
||||||
|
Thread.Sleep(occupyFileFor);
|
||||||
|
fs.Dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
client.Initialize(testProject);
|
||||||
|
var messages = client.DrainAllMessages();
|
||||||
|
if (expectSuccess)
|
||||||
|
{
|
||||||
|
messages.AssertDoesNotContain(MessageTypes.Error);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
messages.ContainsMessage(MessageTypes.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue