示例#1
0
 public SolutionCreator(HostServices hostServices, AssetProvider assetService, Solution baseSolution, CancellationToken cancellationToken)
 {
     _hostServices      = hostServices;
     _assetProvider     = assetService;
     _baseSolution      = baseSolution;
     _cancellationToken = cancellationToken;
 }
示例#2
0
            private async Task <Dictionary <ProjectId, ProjectStateChecksums> > GetProjectMapAsync(
                AssetProvider assetProvider,
                HashSet <Checksum> projects
                )
            {
                var map = new Dictionary <ProjectId, ProjectStateChecksums>();

                var projectChecksums = await assetProvider
                                       .GetAssetsAsync <ProjectStateChecksums>(projects, _cancellationToken)
                                       .ConfigureAwait(false);

                var infos = await assetProvider
                            .GetAssetsAsync <ProjectInfo.ProjectAttributes>(
                    projectChecksums.Select(p => p.Item2.Info),
                    _cancellationToken
                    )
                            .ConfigureAwait(false);

                foreach (var kv in projectChecksums)
                {
                    var info = await assetProvider
                               .GetAssetAsync <ProjectInfo.ProjectAttributes>(
                        kv.Item2.Info,
                        _cancellationToken
                        )
                               .ConfigureAwait(false);

                    map.Add(info.Id, kv.Item2);
                }

                return(map);
            }
示例#3
0
        public SolutionCreator(AssetProvider assetService, Solution baseSolution, CancellationToken cancellationToken)
        {
            Contract.ThrowIfNull(baseSolution);

            _assetProvider     = assetService;
            _baseSolution      = baseSolution;
            _cancellationToken = cancellationToken;
        }
示例#4
0
        /// <summary>
        /// The workspace is designed to be stateless. If someone asks for a solution (through solution checksum),
        /// it will create one and return the solution. The engine takes care of syncing required data and creating a solution
        /// corresponding to the given checksum.
        ///
        /// but doing that from scratch all the time will be expansive in terms of syncing data, compilation being cached, file being parsed
        /// and etc. so even if the service itself is stateless, internally it has several caches to improve perf of various parts.
        ///
        /// first, it holds onto last solution got built. this will take care of common cases where multiple services running off same solution.
        /// second, it uses assets cache to hold onto data just synched (within 3 min) so that if it requires to build new solution,
        ///         it can save some time to re-sync data which might just used by other solution.
        /// third, it holds onto solution from primary branch from Host. and it will try to see whether it can build new solution off the
        ///        primary solution it is holding onto. this will make many solution level cache to be re-used.
        ///
        /// the primary solution can be updated in 2 ways.
        /// first, host will keep track of primary solution changes in host, and call OOP to synch to latest time to time.
        /// second, engine keeps track of whether a certain request is for primary solution or not, and if it is,
        ///         it let that request to update primary solution cache to latest.
        ///
        /// these 2 are complimentary to each other. #1 makes OOP's primary solution to be ready for next call (push), #2 makes OOP's primary
        /// solution be not stale as much as possible. (pull)
        /// </summary>
        private async Task <Solution> CreateSolution_NoLockAsync(
            AssetProvider assetProvider,
            Checksum solutionChecksum,
            bool fromPrimaryBranch,
            int workspaceVersion,
            Solution baseSolution,
            CancellationToken cancellationToken)
        {
            try
            {
                var updater = new SolutionCreator(Services.HostServices, assetProvider, baseSolution, cancellationToken);

                // check whether solution is update to the given base solution
                if (await updater.IsIncrementalUpdateAsync(solutionChecksum).ConfigureAwait(false))
                {
                    // create updated solution off the baseSolution
                    var solution = await updater.CreateSolutionAsync(solutionChecksum).ConfigureAwait(false);

                    if (fromPrimaryBranch)
                    {
                        // if the solutionChecksum is for primary branch, update primary workspace cache with the solution
                        return(UpdateSolutionIfPossible(solution, workspaceVersion));
                    }

                    // otherwise, just return the solution
                    return(solution);
                }

                // we need new solution. bulk sync all asset for the solution first.
                await assetProvider.SynchronizeSolutionAssetsAsync(solutionChecksum, cancellationToken).ConfigureAwait(false);

                // get new solution info and options
                var(solutionInfo, options) = await assetProvider.CreateSolutionInfoAndOptionsAsync(solutionChecksum, cancellationToken).ConfigureAwait(false);

                if (fromPrimaryBranch)
                {
                    // if the solutionChecksum is for primary branch, update primary workspace cache with new solution
                    if (TrySetCurrentSolution(solutionInfo, workspaceVersion, options, out var solution))
                    {
                        return(solution);
                    }
                }

                // otherwise, just return new solution
                var workspace = new TemporaryWorkspace(Services.HostServices, WorkspaceKind.RemoteTemporaryWorkspace, solutionInfo, options);
                return(workspace.CurrentSolution);
            }
            catch (Exception e) when(FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken))
            {
                throw ExceptionUtilities.Unreachable;
            }
        }
示例#5
0
        internal static async Task AssertChecksumsAsync(
            AssetProvider assetService,
            Checksum checksumFromRequest,
            Solution solutionFromScratch,
            Solution incrementalSolutionBuilt)
        {
#if DEBUG
            var sb = new StringBuilder();
            var allChecksumsFromRequest = await GetAllChildrenChecksumsAsync(checksumFromRequest).ConfigureAwait(false);

            var assetMapFromNewSolution = await solutionFromScratch.GetAssetMapAsync(CancellationToken.None).ConfigureAwait(false);

            var assetMapFromIncrementalSolution = await incrementalSolutionBuilt.GetAssetMapAsync(CancellationToken.None).ConfigureAwait(false);

            // check 4 things
            // 1. first see if we create new solution from scratch, it works as expected (indicating a bug in incremental update)
            var mismatch1 = assetMapFromNewSolution.Where(p => !allChecksumsFromRequest.Contains(p.Key)).ToList();
            AppendMismatch(mismatch1, "assets only in new solutoin but not in the request", sb);

            // 2. second check what items is mismatching for incremental solution
            var mismatch2 = assetMapFromIncrementalSolution.Where(p => !allChecksumsFromRequest.Contains(p.Key)).ToList();
            AppendMismatch(mismatch2, "assets only in the incremental solution but not in the request", sb);

            // 3. check whether solution created from scratch and incremental one have any mismatch
            var mismatch3 = assetMapFromNewSolution.Where(p => !assetMapFromIncrementalSolution.ContainsKey(p.Key)).ToList();
            AppendMismatch(mismatch3, "assets only in new solution but not in incremental solution", sb);

            var mismatch4 = assetMapFromIncrementalSolution.Where(p => !assetMapFromNewSolution.ContainsKey(p.Key)).ToList();
            AppendMismatch(mismatch4, "assets only in incremental solution but not in new solution", sb);

            // 4. see what item is missing from request
            var mismatch5 = await GetAssetFromAssetServiceAsync(allChecksumsFromRequest.Except(assetMapFromNewSolution.Keys)).ConfigureAwait(false);

            AppendMismatch(mismatch5, "assets only in the request but not in new solution", sb);

            var mismatch6 = await GetAssetFromAssetServiceAsync(allChecksumsFromRequest.Except(assetMapFromIncrementalSolution.Keys)).ConfigureAwait(false);

            AppendMismatch(mismatch6, "assets only in the request but not in incremental solution", sb);

            var result = sb.ToString();
            if (result.Length > 0)
            {
                Logger.Log(FunctionId.SolutionCreator_AssetDifferences, result);
                Debug.Fail("Differences detected in solution checksum: " + result);
            }
示例#6
0
        public async Task UpdatePrimaryBranchSolutionAsync(AssetProvider assetProvider, Checksum solutionChecksum, int workspaceVersion, CancellationToken cancellationToken)
        {
            var currentSolution = CurrentSolution;

            var currentSolutionChecksum = await currentSolution.State.GetChecksumAsync(cancellationToken).ConfigureAwait(false);

            if (currentSolutionChecksum == solutionChecksum)
            {
                return;
            }

            using (await _availableSolutionsGate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
            {
                var solution = await CreateSolution_NoLockAsync(assetProvider, solutionChecksum, fromPrimaryBranch : true, workspaceVersion, currentSolution, cancellationToken).ConfigureAwait(false);

                _primaryBranchSolutionWithChecksum = Tuple.Create(solutionChecksum, solution);
            }
        }
示例#7
0
        public async ValueTask <Solution> GetSolutionAsync(
            AssetProvider assetProvider,
            Checksum solutionChecksum,
            bool fromPrimaryBranch,
            int workspaceVersion,
            CancellationToken cancellationToken
            )
        {
            var availableSolution = TryGetAvailableSolution(solutionChecksum);

            if (availableSolution != null)
            {
                return(availableSolution);
            }

            // make sure there is always only one that creates a new solution
            using (
                await _availableSolutionsGate
                .DisposableWaitAsync(cancellationToken)
                .ConfigureAwait(false)
                )
            {
                availableSolution = TryGetAvailableSolution(solutionChecksum);
                if (availableSolution != null)
                {
                    return(availableSolution);
                }

                var solution = await CreateSolution_NoLockAsync(
                    assetProvider,
                    solutionChecksum,
                    fromPrimaryBranch,
                    workspaceVersion,
                    CurrentSolution,
                    cancellationToken
                    )
                               .ConfigureAwait(false);

                _lastRequestedSolutionWithChecksum = Tuple.Create(solutionChecksum, solution);

                return(solution);
            }
        }
 public ChecksumSynchronizer(AssetProvider assetProvider)
 {
     _assetProvider = assetProvider;
 }
示例#9
0
 public SolutionService(AssetProvider assetProvider)
 => AssetProvider = assetProvider;
            private static async Task <Dictionary <DocumentId, DocumentStateChecksums> > GetDocumentMapAsync(AssetProvider assetProvider, HashSet <Checksum> documents, CancellationToken cancellationToken)
            {
                var map = new Dictionary <DocumentId, DocumentStateChecksums>();

                var documentChecksums = await assetProvider.GetAssetsAsync <DocumentStateChecksums>(documents, cancellationToken).ConfigureAwait(false);

                var infos = await assetProvider.GetAssetsAsync <DocumentInfo.DocumentAttributes>(documentChecksums.Select(p => p.Item2.Info), cancellationToken).ConfigureAwait(false);

                foreach (var kv in documentChecksums)
                {
                    Debug.Assert(assetProvider.EnsureCacheEntryIfExists(kv.Item2.Info), "Expected the prior call to GetAssetsAsync to obtain all items for this loop.");

                    var info = await assetProvider.GetAssetAsync <DocumentInfo.DocumentAttributes>(kv.Item2.Info, cancellationToken).ConfigureAwait(false);

                    map.Add(info.Id, kv.Item2);
                }

                return(map);
            }
 public SolutionCreator(HostServices hostServices, AssetProvider assetService, Solution baseSolution)
 {
     _hostServices  = hostServices;
     _assetProvider = assetService;
     _baseSolution  = baseSolution;
 }
示例#12
0
        internal static async Task AssertChecksumsAsync(
            AssetProvider assetService,
            Checksum checksumFromRequest,
            Solution solutionFromScratch,
            Solution incrementalSolutionBuilt)
        {
#if DEBUG
            var sb = new StringBuilder();
            var allChecksumsFromRequest = await GetAllChildrenChecksumsAsync(checksumFromRequest).ConfigureAwait(false);

            var assetMapFromNewSolution = await solutionFromScratch.GetAssetMapAsync(includeProjectCones : true, CancellationToken.None).ConfigureAwait(false);

            var assetMapFromIncrementalSolution = await incrementalSolutionBuilt.GetAssetMapAsync(includeProjectCones : true, CancellationToken.None).ConfigureAwait(false);

            // check 4 things
            // 1. first see if we create new solution from scratch, it works as expected (indicating a bug in incremental update)
            var mismatch1 = assetMapFromNewSolution.Where(p => !allChecksumsFromRequest.Contains(p.Key)).ToList();
            AppendMismatch(mismatch1, "assets only in new solutoin but not in the request", sb);

            // 2. second check what items is mismatching for incremental solution
            var mismatch2 = assetMapFromIncrementalSolution.Where(p => !allChecksumsFromRequest.Contains(p.Key)).ToList();
            AppendMismatch(mismatch2, "assets only in the incremental solution but not in the request", sb);

            // 3. check whether solution created from scratch and incremental one have any mismatch
            var mismatch3 = assetMapFromNewSolution.Where(p => !assetMapFromIncrementalSolution.ContainsKey(p.Key)).ToList();
            AppendMismatch(mismatch3, "assets only in new solution but not in incremental solution", sb);

            var mismatch4 = assetMapFromIncrementalSolution.Where(p => !assetMapFromNewSolution.ContainsKey(p.Key)).ToList();
            AppendMismatch(mismatch4, "assets only in incremental solution but not in new solution", sb);

            // 4. see what item is missing from request
            var mismatch5 = await GetAssetFromAssetServiceAsync(allChecksumsFromRequest.Except(assetMapFromNewSolution.Keys)).ConfigureAwait(false);

            AppendMismatch(mismatch5, "assets only in the request but not in new solution", sb);

            var mismatch6 = await GetAssetFromAssetServiceAsync(allChecksumsFromRequest.Except(assetMapFromIncrementalSolution.Keys)).ConfigureAwait(false);

            AppendMismatch(mismatch6, "assets only in the request but not in incremental solution", sb);

            AppendOptionSets();

            var result = sb.ToString();
            if (result.Length > 0)
            {
                Logger.Log(FunctionId.SolutionCreator_AssetDifferences, result);
                Debug.Fail("Differences detected in solution checksum: " + result);
            }

            return;

            void AppendOptionSets()
            {
                var seenChecksums = new HashSet <Checksum>();

                foreach (var list in new[] { mismatch1, mismatch2, mismatch3, mismatch4, mismatch5, mismatch6 })
                {
                    foreach (var(checksum, val) in list)
                    {
                        if (seenChecksums.Add(checksum) && val is SerializableOptionSet optionSet)
                        {
                            sb.AppendLine($"Checksum: {checksum}");
                            sb.AppendLine("Options:");
                            sb.AppendLine(optionSet.GetDebugString());
                            sb.AppendLine();
                        }
                    }
                }
            }