Пример #1
0
        /// <summary>
        /// It's recommended to use this method (or another TryAwaitOrTimeout or AwaitOrTimeout method)
        /// instead of just Task.WhenAny(taskToAwait, Task.Delay(timeout))
        /// because this method cancels the timer for timeout while <c>Task.Delay(timeout)</c>.
        /// If the number of “zombie” timer jobs starts becoming significant, performance could suffer.
        /// For more detailed explanation see https://devblogs.microsoft.com/pfxteam/crafting-a-task-timeoutafter-method/
        /// </summary>
        /// <returns><c>true</c> if <c>taskToAwait</c> completed before the timeout, <c>false</c> otherwise</returns>
        internal static async Task <bool> TryAwaitOrTimeout(this IAgentTimer agentTimer, Task taskToAwait
                                                            , AgentTimeInstant until, CancellationToken cancellationToken = default
                                                            )
        {
            var timeoutDelayCts  = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
            var timeoutDelayTask = agentTimer.Delay(until, timeoutDelayCts.Token);

            try
            {
                var completedTask = await Task.WhenAny(taskToAwait, timeoutDelayTask);

                if (completedTask == taskToAwait)
                {
                    // await taskToAwait to make it throw if taskToAwait is faulted or cancelled so it will throw as it should
                    await taskToAwait;
                    return(true);
                }

                Assertion.IfEnabled?.That(completedTask == timeoutDelayTask
                                          , $"{nameof(completedTask)}: {completedTask}, {nameof(timeoutDelayTask)}: timeOutTask, {nameof(taskToAwait)}: taskToAwait");
                // await timeout task in case it is cancelled and did not timed out so it will throw as it should
                await timeoutDelayTask;
                return(false);
            }
            finally
            {
                if (!timeoutDelayTask.IsCompleted)
                {
                    timeoutDelayCts.Cancel();
                }
                timeoutDelayCts.Dispose();
            }
        }
Пример #2
0
        protected override async Task WorkLoopIteration()
        {
            ++_dbgIterationsCount;
            var                 waitingLogSeverity = LogLevel.Trace;
            WaitInfoS           waitInfo;
            HttpRequestMessage  httpRequest      = null;
            HttpResponseMessage httpResponse     = null;
            string              httpResponseBody = null;

            try
            {
                httpRequest = BuildHttpRequest(_eTag);

                (httpResponse, httpResponseBody) = await FetchConfigHttpResponseAsync(httpRequest);

                ConfigDelta configDelta;
                (configDelta, waitInfo) = ProcessHttpResponse(httpResponse, httpResponseBody);
                if (configDelta != null)
                {
                    UpdateConfigStore(configDelta);
                    _eTag = httpResponse.Headers.ETag;
                }
            }
            catch (OperationCanceledException)
            {
                throw;
            }
            catch (Exception ex)
            {
                var severity = LogLevel.Error;
                waitInfo = new WaitInfoS(WaitTimeIfAnyError, "Default wait time is used because exception was thrown"
                                         + " while fetching configuration from APM Server and parsing it.");

                if (ex is FailedToFetchConfigException fEx)
                {
                    severity = fEx.Severity;
                    fEx.WaitInfo?.Let(it => { waitInfo = it; });
                }

                if (severity == LogLevel.Error)
                {
                    waitingLogSeverity = LogLevel.Information;
                }

                _logger.IfLevel(severity)
                ?.LogException(ex, "Exception was thrown while fetching configuration from APM Server and parsing it."
                               + " ETag: `{ETag}'. URL: `{Url}'. Apm Server base URL: `{ApmServerUrl}'. WaitInterval: {WaitInterval}."
                               + " dbgIterationsCount: {dbgIterationsCount}."
                               + Environment.NewLine + "+-> Request:{HttpRequest}"
                               + Environment.NewLine + "+-> Response:{HttpResponse}"
                               + Environment.NewLine + "+-> Response body [length: {HttpResponseBodyLength}]:{HttpResponseBody}"
                               , _eTag.AsNullableToString(), _getConfigAbsoluteUrl, HttpClientInstance.BaseAddress, waitInfo.Interval.ToHms(),
                               _dbgIterationsCount
                               , httpRequest == null ? " N/A" : Environment.NewLine + TextUtils.Indent(httpRequest.ToString())
                               , httpResponse == null ? " N/A" : Environment.NewLine + TextUtils.Indent(httpResponse.ToString())
                               , httpResponseBody == null ? "N/A" : httpResponseBody.Length.ToString()
                               , httpResponseBody == null ? " N/A" : Environment.NewLine + TextUtils.Indent(httpResponseBody));
            }
            finally
            {
                httpRequest?.Dispose();
                httpResponse?.Dispose();
            }

            _logger.IfLevel(waitingLogSeverity)
            ?.Log("Waiting {WaitInterval}... {WaitReason}. dbgIterationsCount: {dbgIterationsCount}."
                  , waitInfo.Interval.ToHms(), waitInfo.Reason, _dbgIterationsCount);
            await _agentTimer.Delay(_agentTimer.Now + waitInfo.Interval, CtsInstance.Token);
        }