Skip to content

coinprices/PriceRetryPolicy.cpp

Namespaces

Name
sgns

Functions

Name
bool IsTransientTransport(http::ClientError error)
Pre-mapping classifier over the transport's own error enum — true for ClientError::TIMEOUT / CONNECT_FAILED / RESOLVE_FAILED only.
bool IsTransient(const PriceFetchFailure & error)
True only for transport-class failures (D-11): timeouts, connection resets, DNS failures. NEVER true for a status-bearing failure (403/429/any HTTP status), parse errors, or input errors.
RetryDecision ShouldRetry(const PriceFetchFailure & error, int attempt, const RetryConfig & config)
Decide whether to make another attempt after a failure.

Functions Documentation

function IsTransientTransport

bool IsTransientTransport(
    http::ClientError error
)

Pre-mapping classifier over the transport's own error enum — true for ClientError::TIMEOUT / CONNECT_FAILED / RESOLVE_FAILED only.

function IsTransient

bool IsTransient(
    const PriceFetchFailure & error
)

True only for transport-class failures (D-11): timeouts, connection resets, DNS failures. NEVER true for a status-bearing failure (403/429/any HTTP status), parse errors, or input errors.

function ShouldRetry

RetryDecision ShouldRetry(
    const PriceFetchFailure & error,
    int attempt,
    const RetryConfig & config
)

Decide whether to make another attempt after a failure.

Parameters:

  • error The failure from the just-finished attempt
  • attempt 1-based index of the attempt that just failed
  • config The retry schedule in force

Return: Retry iff the error is transient AND attempt < maxAttempts; GiveUp otherwise (403/429 fall through immediately by construction)

Source code

#include "PriceRetryPolicy.hpp"

namespace sgns
{
    bool IsTransientTransport( http::ClientError error )
    {
        switch ( error )
        {
            case http::ClientError::TIMEOUT:
            case http::ClientError::CONNECT_FAILED:
            case http::ClientError::RESOLVE_FAILED:
                return true;
            case http::ClientError::TLS_HANDSHAKE_FAILED:
            case http::ClientError::TLS_CA_LOAD_FAILED:
            case http::ClientError::WRITE_FAILED:
            case http::ClientError::READ_INTERRUPTED:
            case http::ClientError::NO_HEADER:
                break;
        }
        return false;
    }

    bool IsTransient( const PriceFetchFailure &error )
    {
        // Only pure transport failures are retryable (D-11): any HTTP status
        // at all (403/429/404/5xx...) means the server answered — retrying
        // cannot change the answer.
        if ( error.httpStatus != 0 )
        {
            return false;
        }
        if ( error.code != PriceFetchError::NetworkError )
        {
            return false;
        }
        // D-14 — strict transport gating: TLS-handshake/CA-load/write
        // failures stop being blindly retried; only TIMEOUT/CONNECT_FAILED/
        // RESOLVE_FAILED (per IsTransientTransport) are transient. An
        // UNCLASSIFIED {NetworkError, 0} is NOT transient — every facade
        // path populates the field.
        return error.transportError && IsTransientTransport( *error.transportError );
    }

    RetryDecision ShouldRetry( const PriceFetchFailure &error, int attempt, const RetryConfig &config )
    {
        if ( !IsTransient( error ) )
        {
            return RetryDecision::GiveUp;
        }
        if ( attempt >= config.maxAttempts )
        {
            return RetryDecision::GiveUp;
        }
        return RetryDecision::Retry;
    }
} // namespace sgns

Updated on 2026-10-09 at 04:02:50 +0000