Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delete lock files on non-Windows platforms. #4123

Merged
merged 4 commits into from
Jan 14, 2022
Merged

Conversation

tmds
Copy link
Contributor

@tmds tmds commented Jun 23, 2021

Bug

Fixes: NuGet/Home#10679.

Regression? No

Description

// On Non-Windows platforms we use a global mutex for synchronization.
//
// On Windows opening and locking the file are performed atomically,
// and DeleteOnClose deletes the file when there are no more open handles.
//
// On Unix this locking is not implemented atomically, and DeleteOnClose
// deletes ('unlink') the file as soon as the Handle is closed.
//
// The following example shows how deleting the file in Process 1
// allows both Process 2 and Process 3 to obtain a lock because they open
// different file entries.
//
// Process 1    Process 2   Process 3
//   open
//   lock         open
//   unlink
//   unlock                   open
//   close
//                lock        lock

PR Checklist

  • PR has a meaningful title

  • PR has a linked issue.

  • Described changes

  • Tests

    • Automated tests added
    • OR
    • Test exception
    • OR
    • N/A

No new tests added. Functional behavior is covered by existing tests.

  • Documentation
    • Documentation PR or issue filled
    • OR
    • N/A

@tmds tmds requested a review from a team as a code owner June 23, 2021 12:59
@ghost ghost added the Community PRs created by someone not in the NuGet team label Jun 23, 2021
@tmds
Copy link
Contributor Author

tmds commented Jun 23, 2021

When I run ./build.sh on my machine I get an error:

...
EXEC : The path '/home/tmds/repos/NuGet.Client/packages/xunitxml.testlogger/2.0.0/build/_common' specified in the 'TestAdapterPath' is invalid. warning : The custom test adapter search path provided was not found, provide a valid path and try again. [/home/tmds/repos/NuGet.Client/build/build.proj]
/home/tmds/repos/NuGet.Client/build/build.proj(429,5): warning MSB3073: The command "/home/tmds/repos/NuGet.Client/cli/dotnet test /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/Microsoft.Build.NuGetSdkResolver.Test/bin/Release/netcoreapp2.1/Microsoft.Build.NuGetSdkResolver.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/Microsoft.Build.NuGetSdkResolver.Test/bin/Release/netcoreapp5.0/Microsoft.Build.NuGetSdkResolver.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Build.Tasks.Console.Test/bin/Release/netcoreapp2.1/NuGet.Build.Tasks.Console.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Build.Tasks.Console.Test/bin/Release/netcoreapp5.0/NuGet.Build.Tasks.Console.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Build.Tasks.Pack.Test/bin/Release/netcoreapp2.1/NuGet.Build.Tasks.Pack.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Build.Tasks.Pack.Test/bin/Release/netcoreapp5.0/NuGet.Build.Tasks.Pack.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Build.Tasks.Test/bin/Release/netcoreapp2.1/NuGet.Build.Tasks.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Build.Tasks.Test/bin/Release/netcoreapp5.0/NuGet.Build.Tasks.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/bin/Release/netcoreapp2.1/NuGet.CommandLine.Xplat.Tests.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/bin/Release/netcoreapp5.0/NuGet.CommandLine.Xplat.Tests.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Commands.Test/bin/Release/netcoreapp2.1/NuGet.Commands.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Commands.Test/bin/Release/netcoreapp5.0/NuGet.Commands.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Common.Test/bin/Release/netcoreapp2.1/NuGet.Common.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Configuration.Test/bin/Release/netcoreapp2.1/NuGet.Configuration.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.DependencyResolver.Core.Tests/bin/Release/netcoreapp2.1/NuGet.DependencyResolver.Core.Tests.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.DependencyResolver.Core.Tests/bin/Release/netcoreapp5.0/NuGet.DependencyResolver.Core.Tests.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Frameworks.Test/bin/Release/netcoreapp2.1/NuGet.Frameworks.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.LibraryModel.Tests/bin/Release/netcoreapp2.1/NuGet.LibraryModel.Tests.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Packaging.Test/bin/Release/netcoreapp2.1/NuGet.Packaging.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Packaging.Test/bin/Release/netcoreapp5.0/NuGet.Packaging.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/bin/Release/netcoreapp2.1/NuGet.ProjectModel.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/bin/Release/netcoreapp5.0/NuGet.ProjectModel.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/bin/Release/netcoreapp2.1/NuGet.Protocol.Tests.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/bin/Release/netcoreapp5.0/NuGet.Protocol.Tests.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Resolver.Test/bin/Release/netcoreapp2.1/NuGet.Resolver.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Resolver.Test/bin/Release/netcoreapp5.0/NuGet.Resolver.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Shared.Tests/bin/Release/netcoreapp2.1/NuGet.Shared.Tests.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Versioning.Test/bin/Release/netcoreapp2.1/NuGet.Versioning.Test.dll --test-adapter-path:/home/tmds/repos/NuGet.Client/packages/xunitxml.testlogger/2.0.0/build/_common --logger:"xunit;LogFileName=CoreUnitTests-vsts.xml" --logger:"console;verbosity=detailed" --settings:/home/tmds/repos/NuGet.Client/build/xunit.runsettings" exited with code 1.
/home/tmds/repos/NuGet.Client/build/build.proj(429,5): warning MSB4181: The "Exec" task returned false but did not log an error.
/home/tmds/repos/NuGet.Client/build/build.proj(436,5): error : NETCore CoreUnitTests tests failed! Results: /home/tmds/repos/NuGet.Client/build/TestResults/CoreUnitTests-vsts.xml

Copy link
Member

@zivkan zivkan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Firstly, in the future please don't delete the PR template. We have the template because we want people to use it, and we have tools that parse it automating parts of our end to end process (like release notes) that would otherwise be more manual.

Secondly, unfortunately it doesn't work. Steps to reproduce

  1. Either use a Linux VM or WSL2.
  2. git clone https://github.com/NuGet/NuGet.Client
  3. Edit src/NuGet.Core/NuGet.Common/ConcurrencyUtilities.cs to use DeleteOnClose instead of None for Mac/Linux. Also comment out all usage of PerFileLock. (reason: otherwise the filesystem lock is only used in multi-process synchornisation, making it much more difficult to test)
  4. cd /test/NuGet.Core.Tests/NuGet.Common.Tests
  5. dotnet build
  6. run dotnet test --no-build a few times

The tests fail. On my machine, it fails more common than it passes, but because it's a timing issue, the tests do still pass sometimes.

@tmds tmds force-pushed the deleteonclose branch 2 times, most recently from 957d427 to 57276d8 Compare June 28, 2021 03:45
@tmds
Copy link
Contributor Author

tmds commented Jun 28, 2021

@zivkan I'll keep the template next time.
I could reproduce the failing test using the instructions you've provided and I've updated the PR with a fix.

Copy link
Member

@zivkan zivkan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can edit the first comment in this PR and copy the template from here

Good news is that my testing (with only a single process) didn't show any performance regression. Testing methodology using OrchardCore, as it has a large number of projects and packages:

## Test with PR's changes
cd NuGet.Client
git checkout deleteonclose
dotnet build -c Release src/NuGet.Core/NuGet.Build.Tasks

cd ../OrchardCore

# repeat numerous times
git clean -xdf ; dotnet nuget locals all --clear
time dotnet restore -p:NuGetRestoreTargets=/path/to/NuGet.Client/artifacts/NuGet.Build.Tasks/bin/Release/netcoreapp5.0/NuGet.targets

## Test baseline
cd ../NuGet.Client
git checkout HEAD~1
dotnet build -c Release src/NuGet.Core/NuGet.Build.Tasks
cd ../OrchardCore

# repeat numerous times
git clean -xdf ; dotnet nuget locals all --clear
time dotnet restore -p:NuGetRestoreTargets=/path/to/NuGet.Client/artifacts/NuGet.Build.Tasks/bin/Release/netcoreapp5.0/NuGet.targets

On my PC, in WSL2, restore times were the same, before and after.

@tmds tmds changed the title Use DeleteOnClose on Unix Delete lock files on non-Windows platforms. Jun 29, 2021
@tmds
Copy link
Contributor Author

tmds commented Jun 29, 2021

@zivkan I've just realized that this change will break NuGet restores that run in parallel with but do not have this fix. 😞

@zivkan
Copy link
Member

zivkan commented Jun 29, 2021

We can start using the mutex now, but don't delete the file on Linux/Mac unless an environment variable like NUGET_ConcurrencyUtils_DeleteOnClose is set to 1. For net 7 or net 8, we change it to delete by default, but keep the file when the environment variable is set to 0. And finally once the newest version of the .NET SDK that does not use the mutex drops out of LTS, then we can delete the environment variable check in the next major version. I'll need to run the idea past my team, plus the .NET team, but I think it's workable.

@tmds
Copy link
Contributor Author

tmds commented Jul 6, 2021

CI is stuck.

@marcin-krystianc
Copy link
Contributor

CI is stuck.

I think that for this repository the CI needs to be manually run by a repo maintainer 🤷

@tmds
Copy link
Contributor Author

tmds commented Jul 8, 2021

I'm looking at improving what the framework does for DeleteOnClose on non-Windows in dotnet/runtime#55327. That will simply what needs to happen here.

@zivkan
Copy link
Member

zivkan commented Jul 12, 2021

I'm waiting to see how dotnet/runtime#55327 progresses. If that PR gets merged, then this PR doesn't need the global lock or the NoMutex env var.

@zivkan
Copy link
Member

zivkan commented Oct 8, 2021

@tmds congratulations on getting dotnet/runtime#55327 merged! I think this means we don't need to use the global mutex in this PR, it can be a simple on/off switch. What do you think?

@tmds
Copy link
Contributor Author

tmds commented Nov 10, 2021

@tmds congratulations on getting dotnet/runtime#55327 merged! I think this means we don't need to use the global mutex in this PR, it can be a simple on/off switch. What do you think?

Yes, I'll look into this next week.

@tmds
Copy link
Contributor Author

tmds commented Nov 25, 2021

CI is taking long. Is it stuck?

@nkolev92
Copy link
Member

It just doesn't run automatically.
Kicked it off for you.

Copy link
Member

@zivkan zivkan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI is taking long. Is it stuck?

As @nkolev92 mentioned, it doesn't run automatically. For what it's worth, it's documented in our contribuition guidelines document: https://github.com/NuGet/NuGet.Client/blob/dev/CONTRIBUTING.md#contributing-step-by-step

PRs from forks do not trigger CI automatically. Someone in the team needs to apply the "Approved for CI", which will build only the current commit. If changes are pushed to the branch, the "Approved for CI" label needs to be removed and re-applied.

@tmds
Copy link
Contributor Author

tmds commented Nov 30, 2021

@nkolev92 @zivkan I tried to address your feedback. Unfortunately I cannot build or test this on my machine.

The build.sh script doesn't work for me. It fails with:

EXEC : The path '/home/tmds/repos/NuGet.Client/packages/xunitxml.testlogger/2.0.0/build/_common' specified in the 'TestAdapterPath' is invalid. warning : The custom test adapter search path provided was not found, provide a valid path and try again. [/home/tmds/repos/NuGet.Client/build/build.proj]
/home/tmds/repos/NuGet.Client/build/build.proj(431,5): warning MSB3073: The command "/home/tmds/repos/NuGet.Client/cli/dotnet test /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/Microsoft.Build.NuGetSdkResolver.Test/bin/Release/netcoreapp2.1/Microsoft.Build.NuGetSdkResolver.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/Microsoft.Build.NuGetSdkResolver.Test/bin/Release/netcoreapp5.0/Microsoft.Build.NuGetSdkResolver.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Build.Tasks.Console.Test/bin/Release/netcoreapp2.1/NuGet.Build.Tasks.Console.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Build.Tasks.Console.Test/bin/Release/netcoreapp5.0/NuGet.Build.Tasks.Console.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Build.Tasks.Pack.Test/bin/Release/netcoreapp2.1/NuGet.Build.Tasks.Pack.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Build.Tasks.Pack.Test/bin/Release/netcoreapp5.0/NuGet.Build.Tasks.Pack.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Build.Tasks.Test/bin/Release/netcoreapp2.1/NuGet.Build.Tasks.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Build.Tasks.Test/bin/Release/netcoreapp5.0/NuGet.Build.Tasks.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/bin/Release/netcoreapp2.1/NuGet.CommandLine.Xplat.Tests.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/bin/Release/netcoreapp5.0/NuGet.CommandLine.Xplat.Tests.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Commands.Test/bin/Release/netcoreapp2.1/NuGet.Commands.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Commands.Test/bin/Release/netcoreapp5.0/NuGet.Commands.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Common.Test/bin/Release/netcoreapp2.1/NuGet.Common.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Configuration.Test/bin/Release/netcoreapp2.1/NuGet.Configuration.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.DependencyResolver.Core.Tests/bin/Release/netcoreapp2.1/NuGet.DependencyResolver.Core.Tests.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.DependencyResolver.Core.Tests/bin/Release/netcoreapp5.0/NuGet.DependencyResolver.Core.Tests.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Frameworks.Test/bin/Release/netcoreapp2.1/NuGet.Frameworks.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.LibraryModel.Tests/bin/Release/netcoreapp2.1/NuGet.LibraryModel.Tests.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Packaging.Test/bin/Release/netcoreapp2.1/NuGet.Packaging.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Packaging.Test/bin/Release/netcoreapp5.0/NuGet.Packaging.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/bin/Release/netcoreapp2.1/NuGet.ProjectModel.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/bin/Release/netcoreapp5.0/NuGet.ProjectModel.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/bin/Release/netcoreapp2.1/NuGet.Protocol.Tests.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/bin/Release/netcoreapp5.0/NuGet.Protocol.Tests.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Resolver.Test/bin/Release/netcoreapp2.1/NuGet.Resolver.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Resolver.Test/bin/Release/netcoreapp5.0/NuGet.Resolver.Test.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Shared.Tests/bin/Release/netcoreapp2.1/NuGet.Shared.Tests.dll /home/tmds/repos/NuGet.Client/test/NuGet.Core.Tests/NuGet.Versioning.Test/bin/Release/netcoreapp2.1/NuGet.Versioning.Test.dll --test-adapter-path:/home/tmds/repos/NuGet.Client/packages/xunitxml.testlogger/2.0.0/build/_common --logger:"xunit;LogFileName=CoreUnitTests-vsts.xml" --logger:"console;verbosity=detailed" --settings:/home/tmds/repos/NuGet.Client/build/xunit.runsettings" exited with code 1.
/home/tmds/repos/NuGet.Client/build/build.proj(431,5): warning MSB4181: The "Exec" task returned false but did not log an error.
/home/tmds/repos/NuGet.Client/build/build.proj(438,5): error : NETCore CoreUnitTests tests failed! Results: /home/tmds/repos/NuGet.Client/build/TestResults/CoreUnitTests-vsts.xml

When I try to manually run the test project, it requires an EOL .NET version:

Testhost process exited with error: It was not possible to find any compatible framework version
The framework 'Microsoft.NETCore.App', version '2.1.0' was not found.
  - The following frameworks were found:
      3.1.18 at [/usr/lib64/dotnet/shared/Microsoft.NETCore.App]
      5.0.9 at [/usr/lib64/dotnet/shared/Microsoft.NETCore.App]
You can resolve the problem by installing the specified framework and/or SDK.
The specified framework can be found at:
  - https://aka.ms/dotnet-core-applaunch?framework=Microsoft.NETCore.App&framework_version=2.1.0&arch=x64&rid=fedora.35-x64
. Please check the diagnostic logs for more information.

Test Run Aborted.

@tmds
Copy link
Contributor Author

tmds commented Dec 14, 2021

@zivkan I've removed the changes to the test. ptal.

Copy link
Member

@zivkan zivkan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NuGet/nuget-client this PR looks good to me now. Please review if you'd like to provide input before it's merged.

@nkolev92
Copy link
Member

Change looks fine to me.

We should ensure that the tooling will be running on the latest runtime version before merging right?
What would happen if this ran on a runtime without the changes in dotnet/runtime#55327?

@tmds
Copy link
Contributor Author

tmds commented Jan 12, 2022

What would happen if this ran on a runtime without the changes in dotnet/runtime#55327?

This PR isn't introducing a change in behavior unless NUGET_ConcurrencyUtils_DeleteOnClose is set to force delete the files.

When .NET 6.0 goes EOL we can start unconditionally deleting the lock files.

@zivkan
Copy link
Member

zivkan commented Jan 12, 2022

Coincidentally we discussed this PR briefly yesterday. At the time we thought that adding a #if NET7_OR_HIGHER should be added. But now I'm not so sure. I don't like handing customers a foot-gun, but I don't feel good about trying to detect the .NET runtime version during NuGet's runtime either. Plus, it's entirely possible that customers will be using a version of NuGet compiled for .NET 6 or lower, while on the .NET 7 or higher runtime. We're still building NuGet for .NET 5 afterall, not .NET 6.

Right now, I don't have any better ideas than to merge as-is, and if customers shoot themselves in the foot and then complain, we just tell them to stop using the environment variable.

@NuGet/nuget-client again, I plan on merging within a few days unless someone else has feedback they want to provide.

@zivkan zivkan merged commit b56eacc into NuGet:dev Jan 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Community PRs created by someone not in the NuGet team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

NuGetScratch lock files are not cleaned up
7 participants