private async Task DeadLetterMessage(T vstsMessage, IServiceBusMessage message, IDictionary <string, string> eventProperties, string errorMessage, CancellationToken cancellationToken)
        {
            if (!eventProperties.ContainsKey(VstsMessageConstants.ErrorTypePropertyName))
            {
                eventProperties[VstsMessageConstants.ErrorTypePropertyName] = errorMessage;
            }

            await this.TryFailOrchestrationPlan(vstsMessage, cancellationToken).ConfigureAwait(false);

            await clientLogger.LogError("DeadLetterMessage", errorMessage, eventProperties, cancellationToken).ConfigureAwait(false);

            await this.queueClient.DeadLetterAsync(message.GetLockToken()).ConfigureAwait(false);
        }
        public async Task ReceiveAsync(IServiceBusMessage message, CancellationToken cancellationToken)
        {
            // setup basic message properties
            var       messageStopwatch = Stopwatch.StartNew();
            var       eventProperties  = ExtractServiceBusMessageProperties(message);
            Exception exception        = null;
            T         vstsMessage      = null;

            try
            {
                // validate & extract
                string errorMessage;
                if (!ExtractMessage(message, out vstsMessage, out errorMessage))
                {
                    await this.DeadLetterMessage(null, message, eventProperties, errorMessage, cancellationToken).ConfigureAwait(false);

                    await this.StopTimer("MessageExtractionFailed", messageStopwatch, eventProperties, cancellationToken).ConfigureAwait(false);

                    return;
                }

                // merge vsts properties
                foreach (var property in vstsMessage.GetMessageProperties())
                {
                    eventProperties[property.Key] = property.Value;
                }

                // process message
                await this.ProcessMessage(message, this.scheduleHandler, cancellationToken, vstsMessage, eventProperties).ConfigureAwait(false);

                await this.queueClient.CompleteAsync(message.GetLockToken()).ConfigureAwait(false);

                await this.StopTimer("MessageProcessingSucceeded", messageStopwatch, eventProperties, cancellationToken).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                exception = ex;
            }

            // c#6.0 allows await inside catch but this code is not 6.0 yet :-(
            if (exception != null)
            {
                await this.StopTimer("MessageProcessingFailed", messageStopwatch, eventProperties, cancellationToken).ConfigureAwait(false);

                await this.AbandonOrDeadLetterMessage(vstsMessage, message, exception, eventProperties, cancellationToken).ConfigureAwait(false);
            }
        }
        private async Task DelayedAbandon(IServiceBusMessage message, int attempt, Exception exception, IDictionary <string, string> eventProperties, CancellationToken cancellationToken)
        {
            // exponential backoff
            var delayMsecs = this.settings.AbandonDelayMsecs + (1000 * (int)(Math.Pow(2, Math.Max(0, attempt - 1)) - 1));

            delayMsecs = Math.Min(delayMsecs, this.settings.MaxAbandonDelayMsecs);
            var abandoningMessageDueToException = string.Format("Abandoning message due to exception in [{0}]ms", delayMsecs);
            await clientLogger.LogException(exception, "MessageProcessingException", abandoningMessageDueToException, eventProperties, cancellationToken).ConfigureAwait(false);

            while (delayMsecs > 0)
            {
                // await message.RenewLockAsync().ConfigureAwait(false);
                var delay = settings.LockRefreshDelayMsecs == 0 ? 10 : settings.LockRefreshDelayMsecs;
                await Task.Delay(delay, cancellationToken);

                delayMsecs -= delay;
                cancellationToken.ThrowIfCancellationRequested();
            }

            await this.queueClient.AbandonAsync(message.GetLockToken()).ConfigureAwait(false);
        }