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

Improve TryCreateContext #4762

Merged
merged 6 commits into from
Sep 19, 2022
Merged

Conversation

martinrrm
Copy link
Contributor

@martinrrm martinrrm commented Aug 18, 2022

Bug

Fixes: NuGet/Home#11918

Regression? Last working version:

Description

Improvements for TryCreateContext, this is the number 3 fault in PRISM

PRISM data https://prism.vsdata.io/failure/?query=ch%3Drelease%20sev!%3DDiagnostic&eventType=fault&failureType=hits&failureHash=17a271a2-70a5-7dee-8a92-c7db5367242c

Related bug: NuGet/Home#5089

PR Checklist

  • PR has a meaningful title

  • PR has a linked issue.

  • Described changes

  • Tests

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

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

@martinrrm
Copy link
Contributor Author

Conversations in Teams suggested removing throws in this function, since it is a Try___. This will affect other teams; I will start that conversation with them once I have a good improvement in this code path. Please add your suggestions and feedback.

@martinrrm martinrrm marked this pull request as ready for review August 18, 2022 20:31
@martinrrm martinrrm requested a review from a team as a code owner August 18, 2022 20:31
Copy link
Contributor

@donnie-msft donnie-msft left a comment

Choose a reason for hiding this comment

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

I thought we were going to change TryCreateContext (Logs then throws) to instead Log and return false (this is how Try____ methods typically work).

Can you let me know if my understanding is accurate, and how that improves the high number of faults?:

Before this PR:
TryCreateContext (Logs then throws) -> CreatePathContextAsync (Logs then throws) -> GetPathContextFromAssetsFileAsync (Throws without logging)

After this PR:
TryCreateContext (Throws without logging) -> CreatePathContextAsync (Throws without logging) -> GetPathContextFromAssetsFileAsync (Logs and swallows exception)

}
}
catch (Exception e) when (e is KeyNotFoundException || e is InvalidOperationException)
Copy link
Contributor

Choose a reason for hiding this comment

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

Again, this is removing the logging, but still throwing. Can you explain the impact on fault telemetry?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm moving the logging to the function where the throw was being raised. I think we can avoid this try catch if we do that, I dont think Telemetry changes

outputPathContext = NuGetUIThreadHelper.JoinableTaskFactory.Run(
async () =>
{
var nuGetProject = await CreateNuGetProjectAsync(projectUniqueName);
Copy link
Member

Choose a reason for hiding this comment

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

Do we need a generic try/catch here to ensure we never throw?

@martinrrm
Copy link
Contributor Author

@donnie-msft @nkolev92 I reverted changes and I'm only removing the throw and return a null. I'm having trouble trying to improve this code path more, do you have any suggestions? Thanks!

@@ -169,7 +169,8 @@ public bool TryCreateContext(string projectUniqueName, out IVsPathContext output
catch (Exception exception)
{
_telemetryProvider.PostFault(exception, typeof(VsPathContextProvider).FullName);
throw;
outputPathContext = null;
Copy link
Member

Choose a reason for hiding this comment

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

We're still posting a fault so we'd still get noticed in the tracking.

I think we should remove the fault post.

@zivkan any thoughts on whether failure results are any useful here?

Copy link
Member

@zivkan zivkan Aug 25, 2022

Choose a reason for hiding this comment

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

We should remove the fault reporting only from targeted exception handlers, not the generic catch (Exception e) handler. If we know there is a scenario that any fault telemetry is reported (in theory even 1, but in practise we'll probably only investigate when we have a large count) and we think it's a scenario that the customers (other VS extensions) should handle, not our own code, then we need to find a way to catch it in a non-generic exception handler.

The point is, if we add a regression in the future (for example, side-effect from more refactoring of VsSolutionManager, or CpsNuGetProject), we want to get fault telemetry.

Copy link
Member

Choose a reason for hiding this comment

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

So you're suggesting we try to differentiate between an expected failure and one that isn't expected?

So catch InvalidOperationException or whatever the exception for a project not nominated does?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think Nikolche suggestion is really good.

So, because a missing assets file is an error the customer should handle, we can remove the fault telemetry and remove the throw. And if the exception is a different than expected we should report the fault telemetry.

Copy link
Member

Choose a reason for hiding this comment

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

So you're suggesting we try to differentiate between an expected failure and one that isn't expected?

Yes, exactly. This is precisely why last year I added the ProjectNotNominatedException. I created this specific exception which is only used in a well-known scenario, so I can tell the difference between this and other unexpected InvalidOperationException.

I know some people aren't a fan of having hundreds of different exception types, but personally I think it's more important (see also this issue where NuGet.Protocol.dll using FatalProtocolException for many, many different reasons, without being able to programatically distinguish between the different scenarios, makes it harder for us to debug a customer issue).

@martinrrm martinrrm force-pushed the dev-martinrrm-try-create-context-improvements branch from 0c8d5c6 to d15887c Compare August 31, 2022 00:30
Copy link
Contributor

@donnie-msft donnie-msft left a comment

Choose a reason for hiding this comment

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

Looks good!

@donnie-msft donnie-msft dismissed their stale review August 31, 2022 20:08

I thought reviews were automatically dismissed with new commits, so I'm surprised to see my stale review here....

@martinrrm martinrrm force-pushed the dev-martinrrm-try-create-context-improvements branch from d15887c to adf63f4 Compare September 7, 2022 20:13
@martinrrm
Copy link
Contributor Author

@zivkan @nkolev92 @donnie-msft Hi! Can you review the pr again? Thanks!

var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => target.CreatePathContextAsync(project, CancellationToken.None));

// Assert
var exception = await Assert.ThrowsAsync<ProjectNotRestoredException>(() => target.CreatePathContextAsync(project, CancellationToken.None));
Copy link
Member

Choose a reason for hiding this comment

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

Given the XMLDoc says that the exception is InvalidOperationException, I think that the tests should assert the contract, not the implementation, since tests should be validating the customer experience.

Unless the XMLDocs for the methods change, it's not a breaking change from a customer point of view, if a future refactor changes the exception to something other than ProjectNotRestoredException, as long as it's still extending InvalidOperationException.

var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => target.CreatePathContextAsync(project, CancellationToken.None));

// Assert
var exception = await Assert.ThrowsAsync<ProjectNotRestoredException>(() => target.CreatePathContextAsync(project, CancellationToken.None));
Copy link
Member

Choose a reason for hiding this comment

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

The goal of this PR is to stop reporting telemetry when this known error occurs. Therefore, you should have an assert that the INuGetTelemetryProvider's mock is not called.

@ghost ghost added the Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed label Sep 17, 2022
@ghost
Copy link

ghost commented Sep 17, 2022

This PR has been automatically marked as stale because it has no activity for 7 days. It will be closed if no further activity occurs within another 7 days of this comment. If it is closed, you may reopen it anytime when you're ready again, as long as you don't delete the branch.

@martinrrm
Copy link
Contributor Author

I tried adding tests to TryCreateContext but the usage of DTE make it untestable

@ghost ghost removed the Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed label Sep 19, 2022
@martinrrm martinrrm merged commit 3827ea2 into dev Sep 19, 2022
@martinrrm martinrrm deleted the dev-martinrrm-try-create-context-improvements branch September 19, 2022 21:17
AdmiringWorm added a commit to chocolatey/NuGet.Client that referenced this pull request Dec 19, 2022
Insert 6.4.0-rc.123 into rel/d17.4 on 11/07/2022 23:47:12

* tag '6.4.0.123': (60 commits)
  fix a logic error that caused AbandonedMutexException while executing migrations (release-6.4.x) (NuGet#4895)
  unblock source build failing due to fatal: transport 'file' not allowed error (NuGet#4867) (NuGet#4874)
  Signing:  update to August 2022 CTL (NuGet#4791) (NuGet#4850)
  Merged PR 422933: Prefer BCL Directory create API over helper class (7.0.1xx-rc2)
  Fix empty combobox when package is not present in project file (NuGet#4844) (NuGet#4848)
  Fix component detection alert for microsoft.owin package (NuGet#4841) (NuGet#4845)
  Make release label RC, move to escrow mode
  Adds special case to include transitive origins in GetInstalledAndTransitivePackagesAsync API (NuGet#4824)
  Add longPathAware manifest to NuGet.Build.Tasks.Console (NuGet#4830)
  VsPackageInstallerServices should not post ProjectNotNominatedException faults (NuGet#4814)
  Skip test GetOrCreateAsync_WithUnhandledExceptionInPlugin_Throws (NuGet#4831)
  Improve OptProf pipeline job run names (NuGet#4825)
  Increase HttpClientHandler.MaxConnectionsPerServer to 64 to improve PM UI performance in Visual Studio (NuGet#4798)
  Suppress CA2213 warnings to unblock dev branch (NuGet#4823)
  Ensure IsVsOfflineFeed is calculated correctly on 64-bit machines (NuGet#4817)
  Add better handling of AggregateExceptions in static graph-based restore (NuGet#4809)
  Add Component Detection task into each pipeline (NuGet#4813)
  Localizes nuget.exe with default, embedded resource assembly lookup (NuGet#4773)
  Removes BrowseObjectBase class in NuGet Solution Explorer (NuGet#4807)
  Improve TryCreateContext  (NuGet#4762)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Bug]: NuGet.VisualStudio.Implementation.Extensibility.VsPathContextProvider.TryCreateContext fault
4 participants