Skip to content

Always try to use cached data for import completion even if outdated#59760

Merged
genlu merged 22 commits intodotnet:mainfrom
genlu:UseCache
Mar 22, 2022
Merged

Always try to use cached data for import completion even if outdated#59760
genlu merged 22 commits intodotnet:mainfrom
genlu:UseCache

Conversation

@genlu
Copy link
Copy Markdown
Member

@genlu genlu commented Feb 25, 2022

And refresh cache in background.

genlu added 6 commits February 9, 2022 14:21
Even if it might be stale. the completion list would be computed with cached data and cache refresh would be done in background.
Even if it might be stale. the completion list would be computed with cached data and cache refresh would be done in background.
@genlu genlu requested a review from a team as a code owner February 25, 2022 02:18
@ghost ghost added the Area-IDE label Feb 25, 2022
var referencedProjects = graph.GetProjectsThatThisProjectTransitivelyDependsOn(currentProject.Id).SelectAsArray(id => solution.GetRequiredProject(id));
var referencedProjects = graph.GetProjectsThatThisProjectTransitivelyDependsOn(currentProject.Id).Select(id => solution.GetRequiredProject(id)).Where(p => p.SupportsCompilation);

projectsBuilder.Add(currentProject);
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Note that we no longer have any special treatment for current project, i.e. we return whatever in the cache, or if the cache isn't ready, we don't return anything from it.

@genlu genlu requested a review from a team February 28, 2022 20:36
@CyrusNajmabadi
Copy link
Copy Markdown
Contributor

Checking.

{
// Trigger a backgound task to warm up cache and return immediately.
// This won't cause multiple cache update to run simultaneously because WarmUpCacheAsync is implemented by AsyncBatchingWorkQueue.
_ = Task.Run(() => WarmUpCacheAsync(document));
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.

You shouldn't need the Task.Run. You shoudl just add an item to the work queue.

return Task.CompletedTask;

_workQueue.AddWork(project.Id);
return _workQueue.WaitUntilCurrentBatchCompletesAsync();
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.

you definitely don't want to wait on the work to complete :) that somewhat defeats the purpose. you just want to put the item in the queue and then know that it will be taken care of later.

Copy link
Copy Markdown
Member Author

@genlu genlu Mar 1, 2022

Choose a reason for hiding this comment

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

This is the code path that returns the cache update task, not the code path for completion. That said, this can definitely use a little cleanup to make things clearer. Will update

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.

could you point me to the code that looks at the return value of this method?


static bool HasGlobalAlias(MetadataReference? metadataReference)
=> metadataReference != null && (metadataReference.Properties.Aliases.IsEmpty || metadataReference.Properties.Aliases.Any(alias => alias == MetadataReferenceProperties.GlobalAlias));
return (resultBuilder.ToImmutable(), isPartialResult);
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.

just curious what you do with isPArtialResult on the consumption side.

}

private static bool HasGlobalAlias(ImmutableArray<string> aliases)
=> aliases.IsEmpty || aliases.Any(alias => alias == MetadataReferenceProperties.GlobalAlias);
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 needs doc'ing. why does 'aliases.IsEmpty' imply you have global aliases?

foreach (var peReference in GetAllRelevantPeReferences(project))
await SymbolTreeInfo.GetInfoForMetadataReferenceAsync(project.Solution, peReference, loadOnly: false, cancellationToken).ConfigureAwait(false);
}
}
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.

does thsi run in oop? or in VS? If in OOP, i'm ok with this running in parallel over all the PERefs. Unless you want it serial? (i can see args both ways).

Copy link
Copy Markdown
Member Author

@genlu genlu Mar 1, 2022

Choose a reason for hiding this comment

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

This is in OOP (if enabled, that is)

@genlu genlu requested a review from a team as a code owner March 3, 2022 01:52
Copy link
Copy Markdown
Contributor

@sharwell sharwell left a comment

Choose a reason for hiding this comment

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

Missing async tracking

…ionOptions.cs

Co-authored-by: Sam Harwell <sam@tunnelvisionlabs.com>

IDictionary<ProjectId, TProject> ProjectItemsCache { get; }

CancellationToken DisposalToken { get; }
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

All the change in the last commits are refactoring except this and its implementation.

TimeSpan.FromSeconds(1),
BatchUpdateCacheAsync,
listener,
CacheService.DisposalToken);
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Use token for workqueue

TimeSpan.FromSeconds(1),
BatchUpdateCacheAsync,
listenerProvider.GetListener(FeatureAttribute.CompletionSet),
cacheService.DisposalToken);
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Use token for workqueue

// This is the implementation at Editor layer to provide a CancellationToken
// for the workqueue used for background cache refresh.
[ExportWorkspaceServiceFactory(typeof(IImportCompletionCacheService<ExtensionMethodImportCompletionCacheEntry, object>), ServiceLayer.Editor), Shared]
internal sealed class EditorExtensionMethodImportCompletionCacheServiceFactory
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

new Editor layer factory

{
// This is the implementation at Editor layer to provide a CancellationToken
// for the workqueue used for background cache refresh.
[ExportWorkspaceServiceFactory(typeof(IImportCompletionCacheService<TypeImportCompletionCacheEntry, TypeImportCompletionCacheEntry>), ServiceLayer.Editor), Shared]
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

new Editor layer factory

@genlu
Copy link
Copy Markdown
Member Author

genlu commented Mar 16, 2022

Hey @CyrusNajmabadi, another look at this please?

}
}

public class Builder : IDisposable
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.

do you pass your builders around? you coudl consider these being structs too. if you do pass it around, yuo will need to pass by ref given the mutable publicItemCount.

@genlu genlu enabled auto-merge March 18, 2022 22:17
Comment on lines +48 to +49
var exportProvider = (IMefHostExportProvider)project.Solution.Workspace.Services.HostServices;
var listenerProvider = exportProvider.GetExports<AsynchronousOperationListenerProvider>().Single().Value;
Copy link
Copy Markdown
Contributor

@sharwell sharwell Mar 21, 2022

Choose a reason for hiding this comment

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

📝 Code shouldn't assume HostServices implements IMefHostExportProvider. Can we not pass the listener to the constructor of this type?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

You are commenting on an out-of-date snapshot, this is moved to https://github.com/dotnet/roslyn/pull/59760/files#diff-91071542ca3996ad4d7178bc276d40deaae3e2a3b7522531f29d869e1fb636ccR48

That said, I'm still using the same code to fet listener provider there. I assume I can still import the provider in the constructor safely?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed

@genlu genlu dismissed sharwell’s stale review March 22, 2022 00:03

Dismissed since original concern (as well as subsequent comments) have been addressed

@genlu genlu merged commit f690017 into dotnet:main Mar 22, 2022
@ghost ghost added this to the Next milestone Mar 22, 2022
@genlu genlu deleted the UseCache branch March 22, 2022 00:03
@allisonchou allisonchou modified the milestones: Next, 17.2.P3 Mar 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants