public async Task <TaskAgentMessage> GetNextMessageAsync(CancellationToken token) { Trace.Entering(); ArgUtil.NotNull(_session, nameof(_session)); ArgUtil.NotNull(_settings, nameof(_settings)); bool encounteringError = false; int continuousError = 0; string errorMessage = string.Empty; Stopwatch heartbeat = new Stopwatch(); heartbeat.Restart(); while (true) { token.ThrowIfCancellationRequested(); TaskAgentMessage message = null; try { message = await _agentServer.GetAgentMessageAsync(_settings.PoolId, _session.SessionId, _lastMessageId, token); // Decrypt the message body if the session is using encryption message = DecryptMessage(message); if (message != null) { _lastMessageId = message.MessageId; } if (encounteringError) //print the message once only if there was an error { _term.WriteLine(StringUtil.Loc("QueueConnected", DateTime.UtcNow)); encounteringError = false; continuousError = 0; } } catch (OperationCanceledException) when(token.IsCancellationRequested) { Trace.Info("Get next message has been cancelled."); throw; } catch (TaskAgentAccessTokenExpiredException) { Trace.Info("Agent OAuth token has been revoked. Unable to pull message."); throw; } catch (Exception ex) { Trace.Error("Catch exception during get next message."); Trace.Error(ex); // don't retry if SkipSessionRecover = true, DT service will delete agent session to stop agent from taking more jobs. if (ex is TaskAgentSessionExpiredException && !_settings.SkipSessionRecover && await CreateSessionAsync(token)) { Trace.Info($"{nameof(TaskAgentSessionExpiredException)} received, recovered by recreate session."); } else if (!IsGetNextMessageExceptionRetriable(ex)) { throw; } else { continuousError++; //retry after a random backoff to avoid service throttling //in case of there is a service error happened and all agents get kicked off of the long poll and all agent try to reconnect back at the same time. if (continuousError <= 5) { // random backoff [15, 30] _getNextMessageRetryInterval = BackoffTimerHelper.GetRandomBackoff(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(30), _getNextMessageRetryInterval); } else { // more aggressive backoff [30, 60] _getNextMessageRetryInterval = BackoffTimerHelper.GetRandomBackoff(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(60), _getNextMessageRetryInterval); } if (!encounteringError) { //print error only on the first consecutive error _term.WriteError(StringUtil.Loc("QueueConError", DateTime.UtcNow, ex.Message)); encounteringError = true; } // re-create VssConnection before next retry await _agentServer.RefreshConnectionAsync(AgentConnectionType.MessageQueue, TimeSpan.FromSeconds(60)); Trace.Info("Sleeping for {0} seconds before retrying.", _getNextMessageRetryInterval.TotalSeconds); await HostContext.Delay(_getNextMessageRetryInterval, token); } } if (message == null) { if (heartbeat.Elapsed > TimeSpan.FromMinutes(30)) { Trace.Info($"No message retrieved from session '{_session.SessionId}' within last 30 minutes."); heartbeat.Restart(); } else { Trace.Verbose($"No message retrieved from session '{_session.SessionId}'."); } continue; } Trace.Info($"Message '{message.MessageId}' received from session '{_session.SessionId}'."); return(message); } }