internal async Task <bool> GetShouldReplaceDeadSessionOrThrowIfNoRetryAsync(
            QldbTransactionException qte,
            T currentSession,
            int retryAttempt,
            RetryPolicy retryPolicy,
            CancellationToken token)
        {
            bool replaceDeadSession = this.GetIsSessionDeadAndThrowIfNoRetry(
                qte,
                currentSession,
                retryPolicy.MaxRetries,
                retryAttempt);

            try
            {
                var backoffDelay = retryPolicy.BackoffStrategy.CalculateDelay(
                    new RetryPolicyContext(retryAttempt, qte.InnerException));
                await Task.Delay(backoffDelay, token);
            }
            catch (Exception)
            {
                // Safeguard against semaphore leak if parameter actions throw exceptions.
                if (replaceDeadSession)
                {
                    this.poolPermits.Release();
                }

                throw;
            }

            return(replaceDeadSession);
        }
        internal bool GetShouldReplaceDeadSessionOrThrowIfNoRetry(
            QldbTransactionException qte,
            T currentSession,
            int retryAttempt,
            RetryPolicy retryPolicy,
            Action <int> retryAction)
        {
            bool replaceDeadSession = this.GetIsSessionDeadAndThrowIfNoRetry(
                qte,
                currentSession,
                retryPolicy.MaxRetries,
                retryAttempt);

            try
            {
                retryAction?.Invoke(retryAttempt);
                Thread.Sleep(retryPolicy.BackoffStrategy.CalculateDelay(
                                 new RetryPolicyContext(retryAttempt, qte.InnerException)));
            }
            catch (Exception)
            {
                // Safeguard against semaphore leak if parameter actions throw exceptions.
                if (replaceDeadSession)
                {
                    this.poolPermits.Release();
                }

                throw;
            }

            return(replaceDeadSession);
        }
        private bool GetIsSessionDeadAndThrowIfNoRetry(
            QldbTransactionException qte,
            T currentSession,
            int maxRetries,
            int retryAttempt)
        {
            if (qte is RetriableException)
            {
                // Always retry on the first attempt if failure was caused by a stale session in the pool.
                if (qte.InnerException is InvalidSessionException && retryAttempt == 1)
                {
                    this.Logger.LogDebug("Initial session received from pool invalid. Retrying...");
                    return(true);
                }

                // Normal retry logic.
                if (retryAttempt > maxRetries)
                {
                    if (qte.IsSessionAlive)
                    {
                        this.ReleaseSession(currentSession);
                    }
                    else
                    {
                        this.poolPermits.Release();
                    }

                    throw qte.InnerException;
                }

                this.Logger.LogInformation("A recoverable error has occurred. Attempting retry #{}.", retryAttempt);
                this.Logger.LogDebug(
                    "Errored Transaction ID: {}. Error cause: {}",
                    qte.TransactionId,
                    qte.InnerException.ToString());
                if (qte.IsSessionAlive)
                {
                    this.Logger.LogDebug("Retrying with a different session...");
                    this.ReleaseSession(currentSession);
                }
                else
                {
                    this.Logger.LogDebug("Replacing invalid session...");
                }

                return(!qte.IsSessionAlive);
            }
            else
            {
                if (qte.IsSessionAlive)
                {
                    this.ReleaseSession(currentSession);
                }
                else
                {
                    this.poolPermits.Release();
                }

                throw qte.InnerException;
            }
        }