未验证 提交 00933030 编写于 作者: M msftbot[bot] 提交者: GitHub

Merge pull request #42027 from CyrusNajmabadi/solutionSize

Remove solution size tracking from Roslyn.
......@@ -15,7 +15,6 @@
using Microsoft.CodeAnalysis.IncrementalCaches;
using Microsoft.CodeAnalysis.Shared.Options;
using Microsoft.CodeAnalysis.SolutionCrawler;
using Microsoft.CodeAnalysis.SolutionSize;
using Microsoft.CodeAnalysis.Storage;
namespace AnalyzerRunner
......@@ -38,10 +37,12 @@ internal async Task RunAsync(Workspace workspace, CancellationToken cancellation
return;
}
var usePersistentStorage = _options.UsePersistentStorage;
workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options
.WithChangedOption(StorageOptions.SolutionSizeThreshold, _options.UsePersistentStorage ? 0 : int.MaxValue)
.WithChangedOption(SolutionCrawlerOptions.BackgroundAnalysisScopeOption, LanguageNames.CSharp, _options.AnalysisScope)
.WithChangedOption(SolutionCrawlerOptions.BackgroundAnalysisScopeOption, LanguageNames.VisualBasic, _options.AnalysisScope)));
.WithChangedOption(SolutionCrawlerOptions.BackgroundAnalysisScopeOption, LanguageNames.VisualBasic, _options.AnalysisScope)
.WithChangedOption(StorageOptions.Database, usePersistentStorage ? StorageDatabase.SQLite : StorageDatabase.None)));
if (!string.IsNullOrEmpty(_options.ProfileRoot))
{
......@@ -50,26 +51,11 @@ internal async Task RunAsync(Workspace workspace, CancellationToken cancellation
var exportProvider = (IMefHostExportProvider)workspace.Services.HostServices;
var solutionSizeTracker = exportProvider.GetExports<ISolutionSizeTracker>().Single().Value;
// This will return the tracker, since it's a singleton.
var analyzer = ((IIncrementalAnalyzerProvider)solutionSizeTracker).CreateIncrementalAnalyzer(workspace);
await analyzer.NewSolutionSnapshotAsync(workspace.CurrentSolution, cancellationToken).ConfigureAwait(false);
var solutionCrawlerRegistrationService = (SolutionCrawlerRegistrationService)workspace.Services.GetRequiredService<ISolutionCrawlerRegistrationService>();
solutionCrawlerRegistrationService.Register(workspace);
solutionCrawlerRegistrationService.WaitUntilCompletion_ForTestingPurposesOnly(workspace, ImmutableArray.Create(analyzer));
var size = solutionSizeTracker.GetSolutionSize(workspace, workspace.CurrentSolution.Id);
Console.WriteLine("Current solution size:\t" + size);
if (_options.UsePersistentStorage)
if (usePersistentStorage)
{
if (size <= 0)
{
throw new InvalidOperationException("Solution size is too small; persistent storage is disabled.");
}
var persistentStorageService = workspace.Services.GetRequiredService<IPersistentStorageService>();
var persistentStorage = persistentStorageService.GetStorage(workspace.CurrentSolution);
if (persistentStorage is NoOpPersistentStorage)
......
......@@ -12,7 +12,6 @@
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.SolutionSize;
using Microsoft.CodeAnalysis.SQLite;
using Microsoft.CodeAnalysis.Storage;
using Microsoft.CodeAnalysis.Test.Utilities;
......@@ -52,14 +51,12 @@ public void GlobalSetup()
</Project>
</Workspace>");
// Ensure we always use the storage service, no matter what the size of the solution.
_workspace.TryApplyChanges(_workspace.CurrentSolution.WithOptions(
_workspace.CurrentSolution.Options.WithChangedOption(StorageOptions.SolutionSizeThreshold, -1)));
// Explicitly choose the sqlite db to test.
_workspace.TryApplyChanges(_workspace.CurrentSolution.WithOptions(_workspace.Options
.WithChangedOption(StorageOptions.Database, StorageDatabase.SQLite)));
_storageService = new SQLitePersistentStorageService(
_workspace.Services.GetService<IOptionService>(),
new LocationService(),
new SolutionSizeTracker());
_workspace.Services.GetService<IOptionService>(), new LocationService());
_storage = _storageService.GetStorageWorker(_workspace.CurrentSolution);
if (_storage == NoOpPersistentStorage.Instance)
......@@ -123,11 +120,6 @@ public Task PerfAsync()
return Task.WhenAll(tasks);
}
private class SolutionSizeTracker : ISolutionSizeTracker
{
public long GetSolutionSize(Workspace workspace, SolutionId solutionId) => 0;
}
private class LocationService : IPersistentStorageLocationService
{
public bool IsSupported(Workspace workspace) => true;
......
......@@ -11,7 +11,6 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.SolutionSize;
using Microsoft.CodeAnalysis.SQLite;
using Microsoft.CodeAnalysis.Storage;
using Microsoft.CodeAnalysis.Test.Utilities;
......@@ -37,7 +36,6 @@ public enum Size
internal readonly IOptionService _persistentEnabledOptionService = new OptionServiceMock(new Dictionary<IOption, object>
{
{ PersistentStorageOptions.Enabled, true },
{ StorageOptions.SolutionSizeThreshold, 100 }
});
private AbstractPersistentStorageService _storageService;
......@@ -513,16 +511,11 @@ protected Solution CreateOrOpenSolution(bool nullPaths = false)
internal IChecksummedPersistentStorage GetStorage(
Solution solution, IPersistentStorageFaultInjector faultInjectorOpt = null)
{
// For the sake of tests, all solutions are bigger than our threshold, and thus deserve to get storage for them
var solutionSizeTrackerMock = new Mock<ISolutionSizeTracker>();
solutionSizeTrackerMock.Setup(m => m.GetSolutionSize(solution.Workspace, solution.Id))
.Returns(solution.Workspace.Options.GetOption(StorageOptions.SolutionSizeThreshold) + 1);
// If we handed out one for a previous test, we need to shut that down first
_storageService?.GetTestAccessor().Shutdown();
var locationService = new MockPersistentStorageLocationService(solution.Id, _persistentFolder);
_storageService = GetStorageService(locationService, solutionSizeTrackerMock.Object, faultInjectorOpt);
_storageService = GetStorageService(locationService, faultInjectorOpt);
var storage = _storageService.GetStorage(solution, checkBranchId: true);
// If we're injecting faults, we expect things to be strange
......@@ -551,7 +544,7 @@ public string TryGetStorageLocation(Solution solution)
=> solution.Id == _solutionId ? _storageLocation : null;
}
internal abstract AbstractPersistentStorageService GetStorageService(IPersistentStorageLocationService locationService, ISolutionSizeTracker solutionSizeTracker, IPersistentStorageFaultInjector faultInjector);
internal abstract AbstractPersistentStorageService GetStorageService(IPersistentStorageLocationService locationService, IPersistentStorageFaultInjector faultInjector);
protected Stream EncodeString(string text)
{
......
......@@ -4,7 +4,6 @@
using System;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.SolutionSize;
using Microsoft.CodeAnalysis.SQLite;
using Microsoft.CodeAnalysis.Storage;
using Xunit;
......@@ -18,8 +17,8 @@ namespace Microsoft.CodeAnalysis.UnitTests.WorkspaceServices
/// </remarks>
public class SQLitePersistentStorageTests : AbstractPersistentStorageTests
{
internal override AbstractPersistentStorageService GetStorageService(IPersistentStorageLocationService locationService, ISolutionSizeTracker solutionSizeTracker, IPersistentStorageFaultInjector faultInjector)
=> new SQLitePersistentStorageService(_persistentEnabledOptionService, locationService, solutionSizeTracker, faultInjector);
internal override AbstractPersistentStorageService GetStorageService(IPersistentStorageLocationService locationService, IPersistentStorageFaultInjector faultInjector)
=> new SQLitePersistentStorageService(_persistentEnabledOptionService, locationService, faultInjector);
[Fact]
public void TestCrashInNewConnection()
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.SolutionCrawler;
using Microsoft.CodeAnalysis.SolutionSize;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Xunit;
namespace Microsoft.CodeAnalysis.UnitTests.WorkspaceServices
{
[UseExportProvider]
public class SolutionSizeTests
{
[Fact]
public async Task Test_SolutionSize()
{
var expected = 12345;
var solution = CreateSolution(expected);
var analyzer = new SolutionSizeTracker.IncrementalAnalyzer();
// initialize
await analyzer.NewSolutionSnapshotAsync(solution, CancellationToken.None);
await AddSolutionAsync(analyzer, solution);
var size = analyzer.GetSolutionSize(solution.Id);
Assert.Equal(expected, size);
}
[Fact]
public async Task Test_SolutionSize_Update()
{
var expected = 12345;
var solution = CreateSolution(expected);
var analyzer = new SolutionSizeTracker.IncrementalAnalyzer();
// initialize
await analyzer.NewSolutionSnapshotAsync(solution, CancellationToken.None);
await AddSolutionAsync(analyzer, solution);
// update document
var document = solution.Projects.First().Documents.First();
var length = (await document.GetSyntaxTreeAsync()).Length;
var text = SourceText.From(new string('2', 1000));
var newDocument = document.WithText(text);
await analyzer.AnalyzeSyntaxAsync(newDocument, InvocationReasons.DocumentChanged, CancellationToken.None);
var size = analyzer.GetSolutionSize(solution.Id);
Assert.Equal(expected - length + text.Length, size);
}
[Fact]
public async Task Test_RemoveDocument()
{
var expected = 12345;
var solution = CreateSolution(expected);
var analyzer = new SolutionSizeTracker.IncrementalAnalyzer();
// initialize
await analyzer.NewSolutionSnapshotAsync(solution, CancellationToken.None);
await AddSolutionAsync(analyzer, solution);
// remove document
var document = solution.Projects.First().Documents.First();
analyzer.RemoveDocument(document.Id);
var size = analyzer.GetSolutionSize(solution.Id);
var length = (await document.GetSyntaxTreeAsync()).Length;
Assert.Equal(expected - length, size);
}
private static async Task AddSolutionAsync(SolutionSizeTracker.IncrementalAnalyzer analyzer, Solution solution)
{
foreach (var document in solution.Projects.SelectMany(p => p.Documents))
{
await analyzer.AnalyzeSyntaxAsync(document, InvocationReasons.Empty, CancellationToken.None);
}
}
private static Solution CreateSolution(int solutionSize)
{
var info = SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Create());
var workspace = new AdhocWorkspace();
workspace.AddSolution(info);
var solution = workspace.CurrentSolution;
var project = solution.AddProject("proj1", "proj1", LanguageNames.CSharp);
var current = 0;
for (var i = 0; true; i++)
{
var size = current + 1234;
if (current + size >= solutionSize)
{
break;
}
project = project.AddDocument("doc" + i, new string('a', size)).Project;
current += size;
}
var left = solutionSize - current;
if (left > 0)
{
project = project.AddDocument("docLast", new string('a', left)).Project;
}
return project.Solution;
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.CodeAnalysis.SolutionSize
{
internal interface ISolutionSizeTracker
{
long GetSolutionSize(Workspace workspace, SolutionId solutionId);
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Concurrent;
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.SolutionCrawler;
namespace Microsoft.CodeAnalysis.SolutionSize
{
/// <summary>
/// Track approximate solution size.
/// </summary>
[Export(typeof(ISolutionSizeTracker))]
[ExportIncrementalAnalyzerProvider(nameof(SolutionSizeTracker), new[] { WorkspaceKind.Host }), Shared]
internal class SolutionSizeTracker : IIncrementalAnalyzerProvider, ISolutionSizeTracker
{
private readonly IncrementalAnalyzer _tracker = new IncrementalAnalyzer();
[ImportingConstructor]
public SolutionSizeTracker()
{
}
private bool IsSupported(Workspace workspace)
=> workspace.Services.GetRequiredService<IPersistentStorageLocationService>().IsSupported(workspace);
/// <summary>
/// Get approximate solution size at the point of call.
///
/// This API is not supposed to return 100% accurate size.
///
/// if a feature require 100% accurate size, use Solution to calculate it. this API is supposed to
/// lazy and very cheap on answering that question.
/// </summary>
public long GetSolutionSize(Workspace workspace, SolutionId solutionId)
=> IsSupported(workspace) ? _tracker.GetSolutionSize(solutionId) : -1;
IIncrementalAnalyzer IIncrementalAnalyzerProvider.CreateIncrementalAnalyzer(Workspace workspace)
=> IsSupported(workspace) ? _tracker : null;
internal class IncrementalAnalyzer : IIncrementalAnalyzer
{
private readonly ConcurrentDictionary<DocumentId, long> _map = new ConcurrentDictionary<DocumentId, long>(concurrencyLevel: 2, capacity: 10);
private SolutionId _solutionId;
private long _size;
public long GetSolutionSize(SolutionId solutionId)
{
return _solutionId == solutionId ? _size : -1;
}
public Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken)
{
if (_solutionId != solution.Id)
{
Interlocked.Exchange(ref _solutionId, solution.Id);
Interlocked.Exchange(ref _size, 0);
_map.Clear();
}
return Task.CompletedTask;
}
public async Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken)
{
if (!document.SupportsSyntaxTree)
{
return;
}
// getting tree is cheap since tree always stays in memory
var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
var length = tree.Length;
while (true)
{
if (_map.TryAdd(document.Id, length))
{
Interlocked.Add(ref _size, length);
return;
}
if (_map.TryGetValue(document.Id, out var size))
{
if (size == length)
{
return;
}
if (_map.TryUpdate(document.Id, length, size))
{
Interlocked.Add(ref _size, length - size);
return;
}
}
}
}
public void RemoveDocument(DocumentId documentId)
{
if (_map.TryRemove(documentId, out var size))
{
Interlocked.Add(ref _size, -size);
}
}
#region Not Used
public Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public Task DocumentOpenAsync(Document document, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public Task DocumentCloseAsync(Document document, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public Task DocumentResetAsync(Document document, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs e)
{
return false;
}
public Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public void RemoveProject(ProjectId projectId)
{
}
#endregion
}
}
}
......@@ -10,7 +10,6 @@
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.SolutionSize;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Storage
......@@ -23,7 +22,6 @@ internal abstract partial class AbstractPersistentStorageService : IChecksummedP
{
private readonly IOptionService _optionService;
private readonly IPersistentStorageLocationService _locationService;
private readonly ISolutionSizeTracker _solutionSizeTracker;
/// <summary>
/// This lock guards all mutable fields in this type.
......@@ -34,12 +32,10 @@ internal abstract partial class AbstractPersistentStorageService : IChecksummedP
protected AbstractPersistentStorageService(
IOptionService optionService,
IPersistentStorageLocationService locationService,
ISolutionSizeTracker solutionSizeTracker)
IPersistentStorageLocationService locationService)
{
_optionService = optionService;
_locationService = locationService;
_solutionSizeTracker = solutionSizeTracker;
}
protected abstract string GetDatabaseFilePath(string workingFolderPath);
......@@ -76,11 +72,6 @@ internal IChecksummedPersistentStorage GetStorageWorker(Solution solution)
return PersistentStorageReferenceCountedDisposableWrapper.AddReferenceCountToAndCreateWrapper(_currentPersistentStorage);
}
if (!SolutionSizeAboveThreshold(solution))
{
return NoOpPersistentStorage.Instance;
}
var workingFolder = _locationService.TryGetStorageLocation(solution);
if (workingFolder == null)
{
......@@ -127,26 +118,6 @@ private bool DatabaseSupported(Solution solution, bool checkBranchId)
return true;
}
private bool SolutionSizeAboveThreshold(Solution solution)
{
var workspace = solution.Workspace;
if (workspace.Kind == WorkspaceKind.RemoteWorkspace ||
workspace.Kind == WorkspaceKind.RemoteTemporaryWorkspace)
{
// Storage is always available in the remote server.
return true;
}
if (_solutionSizeTracker == null)
{
return false;
}
var size = _solutionSizeTracker.GetSolutionSize(solution.Workspace, solution.Id);
var threshold = this._optionService.GetOption(StorageOptions.SolutionSizeThreshold);
return size >= threshold;
}
private ReferenceCountedDisposable<IChecksummedPersistentStorage> TryCreatePersistentStorage(Solution solution, string workingFolderPath)
{
// Attempt to create the database up to two times. The first time we may encounter
......
......@@ -6,7 +6,7 @@
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.SolutionSize;
// When building for source-build, there is no sqlite dependency
#if !DOTNET_BUILD_FROM_SOURCE
using Microsoft.CodeAnalysis.SQLite;
......@@ -17,12 +17,9 @@ namespace Microsoft.CodeAnalysis.Storage
[ExportWorkspaceServiceFactory(typeof(IPersistentStorageService), ServiceLayer.Desktop), Shared]
internal class PersistenceStorageServiceFactory : IWorkspaceServiceFactory
{
private readonly ISolutionSizeTracker _solutionSizeTracker;
[ImportingConstructor]
public PersistenceStorageServiceFactory(ISolutionSizeTracker solutionSizeTracker)
public PersistenceStorageServiceFactory()
{
_solutionSizeTracker = solutionSizeTracker;
}
public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
......@@ -36,7 +33,7 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
var locationService = workspaceServices.GetService<IPersistentStorageLocationService>();
if (locationService != null)
{
return new SQLitePersistentStorageService(optionService, locationService, _solutionSizeTracker);
return new SQLitePersistentStorageService(optionService, locationService);
}
break;
......
......@@ -8,7 +8,6 @@
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.SolutionSize;
using Microsoft.CodeAnalysis.Storage;
using Roslyn.Utilities;
......@@ -65,18 +64,16 @@ private static bool TryInitializeLibrariesLazy()
public SQLitePersistentStorageService(
IOptionService optionService,
IPersistentStorageLocationService locationService,
ISolutionSizeTracker solutionSizeTracker)
: base(optionService, locationService, solutionSizeTracker)
IPersistentStorageLocationService locationService)
: base(optionService, locationService)
{
}
public SQLitePersistentStorageService(
IOptionService optionService,
IPersistentStorageLocationService locationService,
ISolutionSizeTracker solutionSizeTracker,
IPersistentStorageFaultInjector faultInjector)
: this(optionService, locationService, solutionSizeTracker)
: this(optionService, locationService)
{
_faultInjectorOpt = faultInjector;
}
......
......@@ -12,11 +12,5 @@ internal static class StorageOptions
public static readonly Option<StorageDatabase> Database = new Option<StorageDatabase>(
OptionName, nameof(Database), defaultValue: StorageDatabase.SQLite);
/// <summary>
/// Solution size threshold to start to use a DB (Default: 50MB)
/// </summary>
public static readonly Option<int> SolutionSizeThreshold = new Option<int>(
OptionName, nameof(SolutionSizeThreshold), defaultValue: 50 * 1024 * 1024);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册