/// <summary> /// Uses a <see cref="PrefetchPacksDeserializer"/> to read the packs from the stream. /// </summary> private RetryWrapper <GitObjectsHttpRequestor.GitObjectTaskResult> .CallbackResult DeserializePrefetchPacks( GitEndPointResponseData response, ref long latestTimestamp) { using (ITracer activity = this.Tracer.StartActivity(nameof(this.DeserializePrefetchPacks), EventLevel.Informational)) { PrefetchPacksDeserializer deserializer = new PrefetchPacksDeserializer(response.Stream); foreach (PrefetchPacksDeserializer.PackAndIndex pack in deserializer.EnumeratePacks()) { string packName = string.Format("{0}-{1}-{2}.pack", GVFSConstants.PrefetchPackPrefix, pack.Timestamp, pack.UniqueId); string packFullPath = Path.Combine(this.Enlistment.GitPackRoot, packName); string idxName = string.Format("{0}-{1}-{2}.idx", GVFSConstants.PrefetchPackPrefix, pack.Timestamp, pack.UniqueId); string idxFullPath = Path.Combine(this.Enlistment.GitPackRoot, idxName); EventMetadata data = new EventMetadata(); data["timestamp"] = pack.Timestamp.ToString(); data["uniqueId"] = pack.UniqueId; activity.RelatedEvent(EventLevel.Informational, "Receiving Pack/Index", data); // Write the pack // If it fails, TryWriteNamedPackOrIdx cleans up the packfile and we retry the prefetch if (!this.TryWriteNamedPackOrIdx(activity, pack.PackStream, packFullPath)) { return(new RetryWrapper <GitObjectsHttpRequestor.GitObjectTaskResult> .CallbackResult(null, true)); } // We will try to build an index if the server does not send one if (pack.IndexStream == null) { if (!this.TryBuildIndex(activity, pack, packFullPath)) { return(new RetryWrapper <GitObjectsHttpRequestor.GitObjectTaskResult> .CallbackResult(null, true)); } } else if (!this.TryWriteNamedPackOrIdx(activity, pack.IndexStream, idxFullPath)) { // Try to build the index manually, then retry the prefetch if (this.TryBuildIndex(activity, pack, packFullPath)) { // If we were able to recreate the failed index // we can start the prefetch at the next timestamp latestTimestamp = pack.Timestamp; } // The download stream will not be in a good state if the index download fails. // So we have to restart the prefetch return(new RetryWrapper <GitObjectsHttpRequestor.GitObjectTaskResult> .CallbackResult(null, true)); } latestTimestamp = pack.Timestamp; } return(new RetryWrapper <GitObjectsHttpRequestor.GitObjectTaskResult> .CallbackResult( new GitObjectsHttpRequestor.GitObjectTaskResult(true))); } }
/// <summary> /// Uses a <see cref="PrefetchPacksDeserializer"/> to read the packs from the stream. /// </summary> private RetryWrapper <GitObjectsHttpRequestor.GitObjectTaskResult> .CallbackResult DeserializePrefetchPacks( GitEndPointResponseData response, ref long latestTimestamp, ref long bytesDownloaded, ref List <string> packIndexes, GitProcess gitProcess) { if (packIndexes == null) { packIndexes = new List <string>(); } using (ITracer activity = this.Tracer.StartActivity("DeserializePrefetchPacks", EventLevel.Informational)) { PrefetchPacksDeserializer deserializer = new PrefetchPacksDeserializer(response.Stream); string tempPackFolderPath = Path.Combine(this.Enlistment.GitPackRoot, TempPackFolder); this.fileSystem.CreateDirectory(tempPackFolderPath); List <TempPrefetchPackAndIdx> tempPacks = new List <TempPrefetchPackAndIdx>(); foreach (PrefetchPacksDeserializer.PackAndIndex pack in deserializer.EnumeratePacks()) { // The advertised size may not match the actual, on-disk size. long indexLength = 0; long packLength; // Write the temp and index to a temp folder to avoid putting corrupt files in the pack folder // Once the files are validated and flushed they can be moved to the pack folder string packName = string.Format("{0}-{1}-{2}.pack", GVFSConstants.PrefetchPackPrefix, pack.Timestamp, pack.UniqueId); string packTempPath = Path.Combine(tempPackFolderPath, packName); string idxName = string.Format("{0}-{1}-{2}.idx", GVFSConstants.PrefetchPackPrefix, pack.Timestamp, pack.UniqueId); string idxTempPath = Path.Combine(tempPackFolderPath, idxName); EventMetadata data = CreateEventMetadata(); data["timestamp"] = pack.Timestamp.ToString(); data["uniqueId"] = pack.UniqueId; activity.RelatedEvent(EventLevel.Informational, "Receiving Pack/Index", data); // Write the pack // If it fails, TryWriteTempFile cleans up the file and we retry the prefetch Task packFlushTask; if (!this.TryWriteTempFile(activity, pack.PackStream, packTempPath, out packLength, out packFlushTask)) { bytesDownloaded += packLength; return(new RetryWrapper <GitObjectsHttpRequestor.GitObjectTaskResult> .CallbackResult(null, true)); } bytesDownloaded += packLength; // We will try to build an index if the server does not send one if (pack.IndexStream == null) { GitProcess.Result result; if (!this.TryBuildIndex(activity, packTempPath, out result, gitProcess)) { if (packFlushTask != null) { packFlushTask.Wait(); } // Move whatever has been successfully downloaded so far Exception moveException; this.TryFlushAndMoveTempPacks(tempPacks, ref latestTimestamp, out moveException); return(new RetryWrapper <GitObjectsHttpRequestor.GitObjectTaskResult> .CallbackResult(null, true)); } tempPacks.Add(new TempPrefetchPackAndIdx(pack.Timestamp, packName, packTempPath, packFlushTask, idxName, idxTempPath, idxFlushTask: null)); } else { Task indexFlushTask; if (this.TryWriteTempFile(activity, pack.IndexStream, idxTempPath, out indexLength, out indexFlushTask)) { tempPacks.Add(new TempPrefetchPackAndIdx(pack.Timestamp, packName, packTempPath, packFlushTask, idxName, idxTempPath, indexFlushTask)); } else { bytesDownloaded += indexLength; // Try to build the index manually, then retry the prefetch GitProcess.Result result; if (this.TryBuildIndex(activity, packTempPath, out result, gitProcess)) { // If we were able to recreate the failed index // we can start the prefetch at the next timestamp tempPacks.Add(new TempPrefetchPackAndIdx(pack.Timestamp, packName, packTempPath, packFlushTask, idxName, idxTempPath, idxFlushTask: null)); } else { if (packFlushTask != null) { packFlushTask.Wait(); } } // Move whatever has been successfully downloaded so far Exception moveException; this.TryFlushAndMoveTempPacks(tempPacks, ref latestTimestamp, out moveException); // The download stream will not be in a good state if the index download fails. // So we have to restart the prefetch return(new RetryWrapper <GitObjectsHttpRequestor.GitObjectTaskResult> .CallbackResult(null, true)); } } bytesDownloaded += indexLength; } Exception exception = null; if (!this.TryFlushAndMoveTempPacks(tempPacks, ref latestTimestamp, out exception)) { return(new RetryWrapper <GitObjectsHttpRequestor.GitObjectTaskResult> .CallbackResult(exception, true)); } foreach (TempPrefetchPackAndIdx tempPack in tempPacks) { packIndexes.Add(tempPack.IdxName); } return(new RetryWrapper <GitObjectsHttpRequestor.GitObjectTaskResult> .CallbackResult( new GitObjectsHttpRequestor.GitObjectTaskResult(success: true))); } }