Fix intermittent ApiCompat.Task build failure due to file lock race condition#55109
Conversation
Wraps GetAssemblyVersion in a retry loop (3 attempts with growing delay: 100ms, 200ms, 300ms) to handle transient IOExceptions caused by concurrent MSBuild processes locking the file during parallel builds. The original logic is moved to GetAssemblyVersionCore, and the new GetAssemblyVersion method provides the retry wrapper. This follows the same pattern used by GenerateBundle.cs and GZipCompress.cs in this repo. Fixes #54864 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR adds retry/backoff around FileUtilities.GetAssemblyVersion to reduce intermittent IOException failures when reading assemblies during parallel MSBuild builds (notably when another process temporarily holds an exclusive lock on the DLL).
Changes:
- Introduces a retry loop in
GetAssemblyVersionto handle transientIOExceptionduring file access. - Extracts the original implementation into
GetAssemblyVersionCoreand calls it from the retry wrapper.
|
ApiCompat.Task should not get built twice with different global properties. That's a bin and intermediate clash and should get fixed. |
The ProjectReference from Microsoft.NET.Build.Tasks to ApiCompat.Task was passing its own TargetFramework as a global property. Since ApiCompat.Task is single-TFM (net10.0), this created a duplicate build request with different global properties that wrote to the same intermediate output path, causing a file-locking race condition. Adding GlobalPropertiesToRemove=TargetFramework ensures MSBuild unifies the two build requests into a single cached result, eliminating the concurrent-write race that triggers the IOException. Fixes #54864 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
83b9680 to
4d37449
Compare
Addressed in 4d37449. I left the added retry for defense in-depth. Let me know if you think that is unnecessary. |
There was a problem hiding this comment.
I just looked at a binlog as I was suprirsed that Microsoft.NET.Build.Tasks would pass TargetFramework as a global property to ApiCompat.Task.csproj. The P2P protocol shouldn't do this when it sees a non-multitargeting project. Fortunately it doesn't do that, otherwise we would have a product bug in the P2P protocol:
The extra TargetFramework property is coming from somewhere else:
The SetTargetFramework metadata is incorrect and should be removed. Passing that to a single targeting project is incorrect. We should update our skill to make that clear. EDIT: dotnet/skills#853
|
Is there a reason we need to include |
|
Yes the need for that metadata is all explained in this msbuild skill update: dotnet/skills#853 TL;DR: Without SkipGetTargetFrameworkProperties="true" ResolvePackageAssets errors because a .NETFramework TFM (test project) can't "reference" a .NETCoreApp TFM (task project which is .NETCoreApp only) even if I would love better support here by the .NET SDK. The current P2P protocol doesn't really consider such cases. |
|
I see, I didn't realize that this was a .NETFramework -> .NETCoreApp dependency. |
|
@dsplaisted filed dotnet/msbuild#14250 and would love your input on this |
Summary
Fixes an intermittent build failure where
Microsoft.DotNet.ApiCompat.Taskfails with anIOExceptionbecause its output DLL is locked by a concurrent MSBuild process during parallel builds.Problem
The
GetAssemblyAttributestask intermittently fails in CI whenGetAssemblyVersiontries to read a DLL that is temporarily locked by another concurrent MSBuild process. This manifests as:Impact
This is hitting PR validation and official builds on the order of 20 times per week, requiring build retries each time.
Root cause
Several test projects have
ProjectReferenceentries to single-TFM projects likeMicrosoft.DotNet.ApiCompat.TaskandMicrosoft.DotNet.GenAPI.Taskthat passTargetFrameworkas a global property. Since these referenced projects are single-TFM, this creates a second build request with different global properties that writes to the same intermediate output path. When these two builds run concurrently, one node'sCoreCompilecan overlap with the other's_GetAssemblyInfoFromTemplateFiletarget, hitting the locked DLL.This is the same class of issue already documented in
ApiCompat.Task.csprojitself (see the_AddBuildOutputToPackageCoretarget comment and theCopyPackageToTestPackagesDircomment).Fix
Replaces
SetTargetFramework="TargetFramework=$(NetMinimum)"withSkipGetTargetFrameworkProperties="true" UndefineProperties="TargetFramework"on theProjectReferenceentries in the ApiCompat and GenAPI integration test projects. This prevents MSBuild from passingTargetFrameworkas a global property to the referenced single-TFM projects, ensuring they unify with the solution-direct build and eliminating the concurrent-write race.Fixes #54864