Skip to content

Avoid NuGet restore cycles#8239

Merged
ocallesp merged 4 commits intodotnet:mainfrom
ocallesp:AvoidNuGetRestoreCycles
Aug 12, 2022
Merged

Avoid NuGet restore cycles#8239
ocallesp merged 4 commits intodotnet:mainfrom
ocallesp:AvoidNuGetRestoreCycles

Conversation

@ocallesp
Copy link
Copy Markdown
Contributor

@ocallesp ocallesp commented Jun 11, 2022

Fixes #8326

Avoid infinite NuGet restores.
Scenario:
Projects might have a package that sets AssetTargetsFallback value that can cause a certain PackageReference to be included. This package contains a props file that changed the AssetTargetFallback property value, and the PackageReference Condition was no longer true, causing it to be removed. Once removed, the AssetTargetFallback value from the csproj was used again, causing an infinite restore -> design time build -> restore loop.

This change:
Since DotNet ProjectSystem can detect changes in NuGet restores by calculating a hash, we can use this hash to detect changes in AssetTargetFallBack and check when this value gets repeated.

This pr makes use of the NuGetRestoreCycleDetector to keep the last N hashes, and once it detects a repeated value (cycle) Dotnet ProjectSystem can stop NuGet restores.

Microsoft Reviewers: Open in CodeFlow

@ocallesp ocallesp requested a review from drewnoakes June 11, 2022 01:32
@ocallesp ocallesp changed the title Avoid nu get restore cycles Avoid NuGet restore cycles Jun 11, 2022
@ocallesp ocallesp requested a review from nkolev92 June 13, 2022 18:49
@nkolev92
Copy link
Copy Markdown
Contributor

Any updates on this change @ocallesp?

@ocallesp ocallesp marked this pull request as ready for review August 4, 2022 20:33
@ocallesp ocallesp requested a review from a team as a code owner August 4, 2022 20:33
@MiYanni
Copy link
Copy Markdown
Member

MiYanni commented Aug 5, 2022

@ocalles You're getting build failures with these changes.

(CoreCompile target) -> 
         D:\a\_work\1\s\src\Microsoft.VisualStudio.ProjectSystem.Managed.VS\ProjectSystem\VS\PackageRestore\PackageRestoreDataSource.cs(7,44): error CS0234: The type or namespace name 'Implementation' does not exist in the namespace 'Microsoft.VisualStudio.ProjectSystem' (are you missing an assembly reference?)
         D:\a\_work\1\s\src\Microsoft.VisualStudio.ProjectSystem.Managed.VS\ProjectSystem\VS\PackageRestore\PackageRestoreDataSource.cs(55,26): error CS0246: The type or namespace name 'IInfoBarService' could not be found (are you missing a using directive or an assembly reference?)
         D:\a\_work\1\s\src\Microsoft.VisualStudio.ProjectSystem.Managed.VS\ProjectSystem\VS\PackageRestore\PackageRestoreDataSource.cs(68,13): error CS0246: The type or namespace name 'IInfoBarService' could not be found (are you missing a using directive or an assembly reference?)

@ocallesp ocallesp requested review from jacdavis and tmeschter August 8, 2022 17:43
@ocallesp
Copy link
Copy Markdown
Contributor Author

ocallesp commented Aug 8, 2022

@ocalles You're getting build failures with these changes.

(CoreCompile target) -> 
         D:\a\_work\1\s\src\Microsoft.VisualStudio.ProjectSystem.Managed.VS\ProjectSystem\VS\PackageRestore\PackageRestoreDataSource.cs(7,44): error CS0234: The type or namespace name 'Implementation' does not exist in the namespace 'Microsoft.VisualStudio.ProjectSystem' (are you missing an assembly reference?)
         D:\a\_work\1\s\src\Microsoft.VisualStudio.ProjectSystem.Managed.VS\ProjectSystem\VS\PackageRestore\PackageRestoreDataSource.cs(55,26): error CS0246: The type or namespace name 'IInfoBarService' could not be found (are you missing a using directive or an assembly reference?)
         D:\a\_work\1\s\src\Microsoft.VisualStudio.ProjectSystem.Managed.VS\ProjectSystem\VS\PackageRestore\PackageRestoreDataSource.cs(68,13): error CS0246: The type or namespace name 'IInfoBarService' could not be found (are you missing a using directive or an assembly reference?)

Fixed.
I added the implementation of the InfoBar service as part of this pr.

@ocallesp
Copy link
Copy Markdown
Contributor Author

ocallesp commented Aug 8, 2022

Tests:
Open a solution having an invalid NuGet package. Cycle was detected.
Open roslyn and orchardcore. No cycles detected.

@ocallesp
Copy link
Copy Markdown
Contributor Author

ocallesp commented Aug 8, 2022

Any updates on this change @ocallesp?

Today I add a new commit with the implementation of InfoBar. This pr is complete. Waiting for reviewers and approval

@kvenkatrajan
Copy link
Copy Markdown
Member

@ocallesp - not required for the initial merge but anyways we can add telemetry around this for when we detect a cyclic restore? Will help determine how often our users run into this scenario, we detect it & display the infobar

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

cycledetected

Don't forget to get this blessed once it starts coming through.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

think it would be helpful to add additional properties in addition to cycledetected. So have the event name as :
/nugetrestore/cycledetection and properties like vs.projectsystem.managed.nugetrestore.cycledetection.cycledetected, vs.projectsystem.managed.nugetrestore.cycledetection.durationmillis, vs.projectsystem.managed.nugetrestore.cycledetection.successfulrestorecount,
vs.projectsystem.managed.nugetrestore.cycledetection.projectid or vs.projectsystem.managed.nugetrestore.cycledetection.solutionid perhaps. Any additional items you think may be useful to help troubleshoot when we detect a cyclical restore.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I believe project id and solutionid are sent automatically. Duration and succesffulrestorecount would be nice. I think succesffulrestorecount will be unreliable as I believe we must stop as soon as a cycle is detected.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

if (ErrorHandler.Succeeded(vsShell.GetProperty((int)__VSSPROPID.VSSPROPID_IsInCommandLineMode, out object result)) && (bool)result)

Are we sure this always executes on the ui thread? IIRC, IVsShell requires the ui thread and doesn't have a proxy.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

GetProperty

Same as above. What thread does this execute on? I would feel better about a JTF call to ensure we're on the ui thread.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

if (!_vsShellServices.IsInServerMode)

What is the right behavior if this is in server mode? Right now, it silently succeeds.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Are you sure IsInServerMode works off the ui thread? This is invoked on a arbitrary thread in cycle detector.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

IsInServerMode -> is this to account for Nexus based scenarios? Do we need to explicitly call out a NotSupportedException when IsInServerMode = true?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This was originally to account for Nexus scenarios, yes, and as we don't really have a server SKU under active development it probably doesn't matter much. I think it's reasonable to throw an exception if we are in server mode, and also agree with @jacdavis that we need to verify IsInServerMode is ok to call from an arbitrary thread, and/or transition to the UI thread.

Copy link
Copy Markdown

@jacdavis jacdavis Aug 8, 2022

Choose a reason for hiding this comment

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

Queue

Why are we using a queue here and not some data structure with O(1) lookup? I expected this to be a hash table of hashes. A queue doesn't make a lot of sense in my mind. Am I missing some info? #Closed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I added the hash _lookupTable to improve the search to O(1)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I'm still not convinced a queue is the correct data structure here. Can you please call me to discuss as requested?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This was originally to account for Nexus scenarios, yes, and as we don't really have a server SKU under active development it probably doesn't matter much. I think it's reasonable to throw an exception if we are in server mode, and also agree with @jacdavis that we need to verify IsInServerMode is ok to call from an arbitrary thread, and/or transition to the UI thread.

Comment thread src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/VSShellServices.cs Outdated
Comment thread src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/VSResources.resx Outdated
@ghost ghost added Feature-NuGet NuGet integration including pushing it properties, project and package references, and Pack support. Performance-Scenario-General This issue affects performance in general. Tenet-Performance This issue affects the "Performance" tenet. labels Aug 11, 2022
This a mef exported service that displays an infoBar in the main windows.
The cycle detector is based on hashes calculated from restore inputs.

This algorithm detects patterns A -> B -> A

Time O(1) search
Time O(1) add
Time O(1) delete

Space O(1)
@ocallesp ocallesp merged commit 6618ea8 into dotnet:main Aug 12, 2022
@ghost ghost added this to the 17.4 milestone Aug 12, 2022
@ocallesp
Copy link
Copy Markdown
Contributor Author

Some of the pending code review suggestions will be fixed as part of #8389

drewnoakes added a commit to drewnoakes/project-system that referenced this pull request Sep 19, 2022
Fixes dotnet#8504

This interface and its implementation (`VSShellServices`) was copied from CPS in dotnet#8239, but nothing ever initialised the implementation.

This change changes the API so that these values can be lazily implemented and efficiently queried.
@ocallesp ocallesp deleted the AvoidNuGetRestoreCycles branch September 21, 2022 21:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature-NuGet NuGet integration including pushing it properties, project and package references, and Pack support. Performance-Scenario-General This issue affects performance in general. Tenet-Performance This issue affects the "Performance" tenet.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

VS does not stop restoring

8 participants