예제 #1
0
        private void WriteModuleContent(OciArtifactModuleReference reference, OciArtifactResult result)
        {
            /*
             * this should be kept in sync with the IsModuleRestoreRequired() implementation
             */

            // write main.bicep
            this.fileResolver.Write(this.GetModuleFileUri(reference, ModuleFileType.ModuleMain), result.ModuleStream);

            // write manifest
            // it's important to write the original stream here rather than serialize the manifest object
            // this way we guarantee the manifest hash will match
            this.fileResolver.Write(this.GetModuleFileUri(reference, ModuleFileType.Manifest), result.ManifestStream);

            // write metadata
            var metadata = new ModuleMetadata(result.ManifestDigest);

            using var metadataStream = new MemoryStream();
            OciSerialization.Serialize(metadataStream, metadata);
            metadataStream.Position = 0;
            this.fileResolver.Write(this.GetModuleFileUri(reference, ModuleFileType.Metadata), metadataStream);
        }
예제 #2
0
        private async Task TryWriteModuleContentAsync(OciArtifactModuleReference reference, OciArtifactResult result)
        {
            // this has to be after downloading the manifest so we don't create directories for non-existent modules
            string modulePath = GetModuleDirectoryPath(reference);

            // creating the directory doesn't require locking
            CreateModuleDirectory(modulePath);

            /*
             * We have already downloaded the module content from the registry.
             * The following sections will attempt to synchronize the module write with other
             * instances of the language server running on the same machine.
             *
             * We are not trying to prevent tampering with the module cache by the user.
             */

            Uri lockFileUri = GetModuleFileUri(reference, ModuleFileType.Lock);
            var sw          = Stopwatch.StartNew();

            while (sw.Elapsed < ModuleDirectoryContentionTimeout)
            {
                var @lock = this.fileResolver.TryAcquireFileLock(lockFileUri);
                using (@lock)
                {
                    // the placement of "if" inside "using" guarantees that even an exception thrown by the condition results in the lock being released
                    // (current condition can't throw, but this potentially avoids future regression)
                    if (@lock is not null)
                    {
                        // we have acquired the lock
                        if (!this.IsModuleRestoreRequired(reference))
                        {
                            // the other instance has already written out the content to disk - we can discard the content we downloaded
                            return;
                        }

                        // write the contents to disk
                        this.WriteModuleContent(reference, result);
                        return;
                    }
                }

                // we have not acquired the lock - let's give the instance that has the lock some time to finish writing the content to the directory
                // (the operation involves only writing the already downloaded content to disk, so it "should" complete fairly quickly)
                await Task.Delay(ModuleDirectoryContentionRetryInterval);
            }

            // we have exceeded the timeout
            throw new OciModuleRegistryException($"Exceeded the timeout of \"{ModuleDirectoryContentionTimeout}\" to acquire the lock on file \"{lockFileUri}\".");
        }