Example #1
0
        public HgResumeApiResponse Execute(string method, HgResumeApiParameters request, byte[] bytesToWrite, int secondsBeforeTimeout)
        {
            ValidateParameters(method, request, bytesToWrite, secondsBeforeTimeout);
            if (method == "getRevisions")
            {
                IEnumerable<Revision> revisions = _repo.GetAllRevisions();

                if (revisions.Count() == 0)
                {
                    return ApiResponses.Revisions("0:default");
                }
                var revisionStrings = _repo.GetAllRevisions().Select(rev => rev.Number.Hash + ':' + rev.Branch);
                return ApiResponses.Revisions(string.Join("|", revisionStrings.ToArray()));
            }
            if (method == "finishPushBundle")
            {
                return ApiResponses.PushComplete();
            }
            if (method == "pushBundleChunk")
            {
                _executeCount++;
                if (_cancelCount == _executeCount)
                {
                    _progress.CancelRequested = true;
                    return ApiResponses.Failed("");
                }
                if (_failCount == _executeCount)
                {
                    return ApiResponses.Failed("");
                }
                if (_timeoutList.Contains(_executeCount))
                {
                    return null;
                }
                if (_serverUnavailableList.Any(i => i.ExecuteCount == _executeCount))
                {
                    return ApiResponses.NotAvailable(
                            _serverUnavailableList.Where(i => i.ExecuteCount == _executeCount).First().Message
                            );
                }
                _helper = new PullStorageManager(_localStorage.Path, request.TransId);

                //int bundleSize = Convert.ToInt32(parameters["bundleSize"]);
                //int offset = Convert.ToInt32(parameters["offset"]);
                //int chunkSize = bytesToWrite.Length;

                _helper.WriteChunk(request.StartOfWindow, bytesToWrite);

                if (request.StartOfWindow + request.ChunkSize == request.BundleSize)
                {
                    if (_repo.Unbundle(_helper.BundlePath))
                    {
                        return ApiResponses.PushComplete();
                    }
                    return ApiResponses.Reset();
                }
                if (request.StartOfWindow + request.ChunkSize < request.BundleSize)
                {
                    return ApiResponses.PushAccepted(_helper.StartOfWindow);
                }
                return ApiResponses.Failed("offset + chunkSize > bundleSize !");
            }
            return ApiResponses.Custom(HttpStatusCode.InternalServerError);
        }
Example #2
0
        public bool Pull(string[] baseRevisions)
        {
            var    tipRevision = _repo.GetTip();
            var    localTip    = "0";
            string errorMessage;

            if (tipRevision != null)
            {
                localTip = tipRevision.Number.Hash;
            }

            if (baseRevisions.Length == 0)
            {
                errorMessage = "Pull failed: No base revision.";
                _progress.WriteError(errorMessage);
                throw new HgResumeOperationFailed(errorMessage);
            }

            var bundleHelper = new PullStorageManager(PathToLocalStorage, GetBundleIdFilenameBase("pull", baseRevisions, localTip));
            var req          = new HgResumeApiParameters
            {
                RepoId        = _apiServer.ProjectId,
                BaseHashes    = baseRevisions,
                TransId       = bundleHelper.TransactionId,
                StartOfWindow = bundleHelper.StartOfWindow,
                ChunkSize     = InitialChunkSize
            };
            int bundleSize = 0;

            int  loopCtr = 1;
            bool retryLoop;

            do
            {
                if (_progress.CancelRequested)
                {
                    throw new UserCancelledException();
                }
                retryLoop = false;
                var response = PullOneChunk(req);
                if (response.Status == PullStatus.Unauthorized)
                {
                    throw new UnauthorizedAccessException();
                }
                if (response.Status == PullStatus.NotAvailable)
                {
                    _progress.ProgressIndicator.Initialize();
                    _progress.ProgressIndicator.Finish();
                    return(false);
                }
                if (response.Status == PullStatus.Timeout)
                {
                    _progress.WriteWarning("Pull operation timed out.  Retrying...");
                    retryLoop = true;
                    continue;
                }
                if (response.Status == PullStatus.NoChange)
                {
                    _progress.WriteMessage("No changes");
                    bundleHelper.Cleanup();
                    _progress.ProgressIndicator.Initialize();
                    _progress.ProgressIndicator.Finish();
                    return(false);
                }
                if (response.Status == PullStatus.InProgress)
                {
                    _progress.WriteStatus("Preparing data on server");
                    retryLoop = true;
                    // advance the progress bar 2% to show that something is happening
                    _progress.ProgressIndicator.PercentCompleted = _progress.ProgressIndicator.PercentCompleted + 2;
                    continue;
                }
                if (response.Status == PullStatus.InvalidHash)
                {
                    // this should not happen...but sometimes it gets into a state where it remembers the wrong basehash of the server (CJH Feb-12)
                    retryLoop = true;
                    _progress.WriteVerbose("Invalid basehash response received from server... clearing cache and retrying");
                    req.BaseHashes = GetHashStringsFromRevisions(GetCommonBaseHashesWithRemoteRepo(false));
                    continue;
                }
                if (response.Status == PullStatus.Fail)
                {
                    // No need to abort the attempts just because .Net web request says so. See Push also. CP 2012-06
                    _progress.WriteWarning("Pull data chunk failed");
                    retryLoop         = true;
                    req.StartOfWindow = bundleHelper.StartOfWindow;
                    continue;
                }
                if (response.Status == PullStatus.Reset)
                {
                    retryLoop = true;
                    bundleHelper.Reset();
                    _progress.WriteVerbose("Server's bundle cache has expired.  Restarting pull...");
                    req.StartOfWindow = bundleHelper.StartOfWindow;
                    continue;
                }

                //bundleSizeFromResponse = response.BundleSize;
                if (loopCtr == 1)
                {
                    _progress.ProgressIndicator.Initialize();
                    if (req.StartOfWindow != 0)
                    {
                        string message = String.Format("Resuming pull operation at {0} received", GetHumanReadableByteSize(req.StartOfWindow));
                        _progress.WriteVerbose(message);
                    }
                }

                bundleHelper.WriteChunk(req.StartOfWindow, response.Chunk);
                req.StartOfWindow = req.StartOfWindow + response.Chunk.Length;
                req.ChunkSize     = response.ChunkSize;
                if (bundleSize == response.BundleSize && bundleSize != 0)
                {
                    _progress.ProgressIndicator.PercentCompleted = (int)((long)req.StartOfWindow * 100 / bundleSize);
                    string eta = CalculateEstimatedTimeRemaining(bundleSize, req.ChunkSize, req.StartOfWindow);
                    _progress.WriteStatus(string.Format("Receiving {0} {1}", GetHumanReadableByteSize(bundleSize), eta));
                }
                else
                {
                    // this is only useful when the bundle size is significantly large (like with a clone operation) such that
                    // the server takes a long time to create the bundle, and the bundleSize continues to rise as the chunks are received
                    bundleSize = response.BundleSize;
                    _progress.WriteStatus(string.Format("Preparing data on server (>{0})", GetHumanReadableByteSize(bundleSize)));
                }
                loopCtr++;
            } while (req.StartOfWindow < bundleSize || retryLoop);

            if (_repo.Unbundle(bundleHelper.BundlePath))
            {
                _progress.WriteMessage("Pull operation completed successfully");
                _progress.ProgressIndicator.Finish();
                _progress.WriteMessage("Finished Receiving");
                bundleHelper.Cleanup();
                var response = FinishPull(req.TransId);
                if (response == PullStatus.Reset)
                {
                    /* Calling Pull recursively to finish up another pull will mess up the ProgressIndicator.  This case is
                     * // rare enough that it's not worth trying to get the progress indicator working for a recursive Pull()
                     */
                    _progress.WriteMessage("Remote repo has changed.  Initiating additional pull operation");
                    GetCommonBaseHashesWithRemoteRepo(false);                     //Rebuild cache with current server information
                    return(Pull());
                }

                // REVIEW: I'm not sure why this was set to the server tip before, if we just pulled then won't our head
                // be the correct common base? Maybe not if a merge needs to happen,
                LastKnownCommonBases = new List <Revision>(_repo.BranchingHelper.GetBranches());
                return(true);
            }
            _progress.WriteError("Received all data but local unbundle operation failed or resulted in multiple heads!");
            _progress.ProgressIndicator.Finish();
            bundleHelper.Cleanup();
            errorMessage = "Pull operation failed";
            _progress.WriteError(errorMessage);
            throw new HgResumeOperationFailed(errorMessage);
        }
Example #3
0
        public bool Pull(string[] baseRevisions)
        {
            var tipRevision = _repo.GetTip();
            string localTip = "0";
            string errorMessage;
            if (tipRevision != null)
            {
                localTip = tipRevision.Number.Hash;
            }

            if (baseRevisions.Length == 0)
            {
                errorMessage = "Pull failed: No base revision.";
                _progress.WriteError(errorMessage);
                throw new HgResumeOperationFailed(errorMessage);
            }

            string bundleId = "";
            foreach (var revision in baseRevisions)
            {
                bundleId += revision + "_" + localTip + '-';
            }
            bundleId = bundleId.TrimEnd('-');
            var bundleHelper = new PullStorageManager(PathToLocalStorage, bundleId);
            var req = new HgResumeApiParameters
                      {
                          RepoId = _apiServer.ProjectId,
                          BaseHashes = baseRevisions,
                          TransId = bundleHelper.TransactionId,
                          StartOfWindow = bundleHelper.StartOfWindow,
                          ChunkSize = InitialChunkSize
                      };
            int bundleSize = 0;

            int loopCtr = 1;
            bool retryLoop;

            do
            {
                if (_progress.CancelRequested)
                {
                    throw new UserCancelledException();
                }
                retryLoop = false;
                var response = PullOneChunk(req);
                if (response.Status == PullStatus.Unauthorized)
                {
                    throw new UnauthorizedAccessException();
                }
                if (response.Status == PullStatus.NotAvailable)
                {
                    _progress.ProgressIndicator.Initialize();
                    _progress.ProgressIndicator.Finish();
                    return false;
                }
                if (response.Status == PullStatus.Timeout)
                {
                    _progress.WriteWarning("Pull operation timed out.  Retrying...");
                    retryLoop = true;
                    continue;
                }
                if (response.Status == PullStatus.NoChange)
                {
                    _progress.WriteMessage("No changes");
                    bundleHelper.Cleanup();
                    _progress.ProgressIndicator.Initialize();
                    _progress.ProgressIndicator.Finish();
                    return false;
                }
                if (response.Status == PullStatus.InProgress)
                {
                    _progress.WriteStatus("Preparing data on server");
                    retryLoop = true;
                    // advance the progress bar 2% to show that something is happening
                    _progress.ProgressIndicator.PercentCompleted = _progress.ProgressIndicator.PercentCompleted + 2;
                    continue;
                }
                if (response.Status == PullStatus.InvalidHash)
                {
                    // this should not happen...but sometimes it gets into a state where it remembers the wrong basehash of the server (CJH Feb-12)
                    retryLoop = true;
                    _progress.WriteVerbose("Invalid basehash response received from server... clearing cache and retrying");
                    req.BaseHashes = GetHashStringsFromRevisions(GetCommonBaseHashesWithRemoteRepo(false));
                    continue;
                }
                if (response.Status == PullStatus.Fail)
                {
                    // Not sure that we need to abort the attempts just because .Net web request says so.  See Push also. CP 2012-06
                    errorMessage = "Pull data chunk failed";
                    _progress.WriteError(errorMessage);
                    retryLoop = true;
                    req.StartOfWindow = bundleHelper.StartOfWindow;
                    //_progress.ProgressIndicator.Initialize();
                    //_progress.ProgressIndicator.Finish();
                    //throw new HgResumeOperationFailed(errorMessage);
                    continue;
                }
                if (response.Status == PullStatus.Reset)
                {
                    retryLoop = true;
                    bundleHelper.Reset();
                    _progress.WriteVerbose("Server's bundle cache has expired.  Restarting pull...");
                    req.StartOfWindow = bundleHelper.StartOfWindow;
                    continue;
                }

                //bundleSizeFromResponse = response.BundleSize;
                if (loopCtr == 1)
                {
                    _progress.ProgressIndicator.Initialize();
                    if (req.StartOfWindow != 0)
                    {
                        string message = String.Format("Resuming pull operation at {0} received", GetHumanReadableByteSize(req.StartOfWindow));
                        _progress.WriteVerbose(message);
                    }
                }

                bundleHelper.WriteChunk(req.StartOfWindow, response.Chunk);
                req.StartOfWindow = req.StartOfWindow + response.Chunk.Length;
                req.ChunkSize = response.ChunkSize;
                if (bundleSize == response.BundleSize && bundleSize != 0)
                {
                    _progress.ProgressIndicator.PercentCompleted = (int)((long)req.StartOfWindow * 100 / bundleSize);
                    string eta = CalculateEstimatedTimeRemaining(bundleSize, req.ChunkSize, req.StartOfWindow);
                    _progress.WriteStatus(string.Format("Receiving {0} {1}", GetHumanReadableByteSize(bundleSize), eta));
                }
                else
                {
                    // this is only useful when the bundle size is significantly large (like with a clone operation) such that
                    // the server takes a long time to create the bundle, and the bundleSize continues to rise as the chunks are received
                    bundleSize = response.BundleSize;
                    _progress.WriteStatus(string.Format("Preparing data on server (>{0})", GetHumanReadableByteSize(bundleSize)));
                }
                loopCtr++;

            } while (req.StartOfWindow < bundleSize || retryLoop);

            if (_repo.Unbundle(bundleHelper.BundlePath))
            {
                _progress.WriteMessage("Pull operation completed successfully");
                _progress.ProgressIndicator.Finish();
                _progress.WriteMessage("Finished Receiving");
                bundleHelper.Cleanup();
                var response = FinishPull(req.TransId);
                if (response == PullStatus.Reset)
                {
                    /* Calling Pull recursively to finish up another pull will mess up the ProgressIndicator.  This case is
                    // rare enough that it's not worth trying to get the progress indicator working for a recursive Pull()
                    */
                    _progress.WriteMessage("Remote repo has changed.  Initiating additional pull operation");
                    return Pull();
                }

                // REVIEW: I'm not sure why this was set to the server tip before, if we just pulled then won't our head
                // be the correct common base? Maybe not if a merge needs to happen,
                LastKnownCommonBases = new List<Revision>(_repo.BranchingHelper.GetBranches());
                return true;
            }
            _progress.WriteError("Received all data but local unbundle operation failed or resulted in multiple heads!");
            _progress.ProgressIndicator.Finish();
            bundleHelper.Cleanup();
            errorMessage = "Pull operation failed";
            _progress.WriteError(errorMessage);
            throw new HgResumeOperationFailed(errorMessage);
        }
 public void StartOfWindow_NoWrites_Zero()
 {
     using (var e = new PullStorageEnvForTest())
     {
         var bundleHelper = new PullStorageManager(e.BaseFolder.Path, "randomHash");
         Assert.That(bundleHelper.StartOfWindow, Is.EqualTo(0));
     }
 }
        public void WriteChunk_WriteMultipleChunks_AssembledOk()
        {
            using (var e = new PullStorageEnvForTest())
            {
                var bundleHelper = new PullStorageManager(e.BaseFolder.Path, "randomHash");
                string[] sampleTexts = new[] { "this is a sentence. ", "Another sentence. ", "Yet another word string." };

                string combinedText = "";
                int startOfWindow = 0;
                foreach (var sampleText in sampleTexts)
                {
                    combinedText += sampleText;
                    byte[] bytes = Encoding.UTF8.GetBytes(sampleText);
                    bundleHelper.WriteChunk(startOfWindow, bytes);
                    startOfWindow += bytes.Length;
                }
                Assert.That(combinedText, Is.EqualTo(File.ReadAllText(bundleHelper.BundlePath)));
            }
        }
 public void WriteChunk_Text_WriteOk()
 {
     using (var e = new PullStorageEnvForTest())
     {
         var bundleHelper = new PullStorageManager(e.BaseFolder.Path, "randomHash");
         string sampleText = "some sample text";
         byte[] bytes = Encoding.UTF8.GetBytes(sampleText);
         bundleHelper.WriteChunk(0, bytes);
         string fromFile = File.ReadAllText(bundleHelper.BundlePath);
         Assert.That(sampleText, Is.EqualTo(fromFile));
     }
 }
 public void StartOfWindow_WriteBytes_LengthOfBytes()
 {
     using (var e = new PullStorageEnvForTest())
     {
         var bundleHelper = new PullStorageManager(e.BaseFolder.Path, "randomHash");
         string sampleText = "some sample text";
         byte[] bytes = Encoding.UTF8.GetBytes(sampleText);
         bundleHelper.WriteChunk(0, bytes);
         Assert.That(bundleHelper.StartOfWindow, Is.EqualTo(bytes.Length));
     }
 }