nx_retry_policy.nx
buildroot/runtime/nx_retry_policy.nx
about
nx_retry_policy.nx -- exponential backoff + jitter + max-attempts.
module: nishi-core.ingest.retry_policy
depends: nishi-core.io.syscalls, nishi-core.io.iso8601
disk_kb: 5
capability: CORE_IO
license_tier: PUBLIC_NISHI_SUBSTRATE
genealogy_id: aws_architecture_blog_exponential_backoff_jitter_2015 +
polly_microsoft_resilience_library +
google_sre_book_retry_amplification +
nishi_ingestion_s_class_cardinal_2026
Retry primitive with exponential backoff + jitter + max-attempts.
Composes against nx_circuit_breaker (trips when retries exhausted).
Substrate Cardinal 14 graceful-degradation: every NX-INGEST adapter
invokes this primitive to decide whether to retry a failed
upstream call + how long to wait.
===== Why exponential backoff + jitter ===========================
AWS Architectural Blog 2015: when many clients retry simultaneously
(thundering herd), they amplify the upstream load just as upstream
is recovering. Exponential backoff alone DOESN'T fix this — all
clients still retry at the same exponentially-spaced times.
JITTER (random offset within the backoff window) breaks the
synchronization.
Google SRE book: retry amplification is the #1 cause of cascading
outages. Bounded max-attempts + jittered backoff prevents
substrate from contributing to upstream failure.
===== Backoff formula ============================================
attempt_n: wait base * 2^n milliseconds, capped at max_wait_seconds
jitter: rs_jitter_ms bounds the delay to [d/2, d]
total: sum across attempts capped at attempts_cap
===== CORRECTED 2026-08-21 -- THIS BLOCK DESCRIBED A THING THE CODE DID NOT DO ====
dependencies 3 imports · 1 importers
imports: nx_syscalls.nxnx_iso8601.nxnx_restart_strategy.nx
imported by: nx_retry_policy_gate.nx
structs
| 137 | struct RetryPolicy |
consts
| 79 | const NX_RETRY_PROCEED: i64 = 1 // try the upstream call |
| 80 | const NX_RETRY_WAIT_AND_RETRY: i64 = 2 // wait then retry |
| 81 | const NX_RETRY_EXHAUSTED: i64 = 3 // max attempts reached; give up |
| 82 | const NX_RETRY_NON_RETRYABLE: i64 = 4 // error class not retryable (auth fail etc.) |
| 83 | const NX_RETRY_CIRCUIT_OPEN: i64 = 5 // breaker says short-circuit |
| 100 | const NX_RETRY_ERROR_TIMEOUT: i64 = 1 // retryable |
| 101 | const NX_RETRY_ERROR_CONN_REFUSED: i64 = 2 // retryable |
| 102 | const NX_RETRY_ERROR_5XX_SERVER: i64 = 3 // retryable (server-side) |
| 103 | const NX_RETRY_ERROR_429_RATE_LIMIT: i64 = 4 // retryable with Retry-After |
| 104 | const NX_RETRY_ERROR_4XX_CLIENT: i64 = 5 // NOT retryable (our request bad) |
| 105 | const NX_RETRY_ERROR_401_AUTH: i64 = 6 // NOT retryable (need new creds) |
| 106 | const NX_RETRY_ERROR_403_FORBIDDEN: i64 = 7 // NOT retryable |
| 107 | const NX_RETRY_ERROR_404_NOT_FOUND: i64 = 8 // NOT retryable (doesn't exist) |
| 108 | const NX_RETRY_ERROR_TLS_FAIL: i64 = 9 // retryable (transient TLS issue) |
| 109 | const NX_RETRY_ERROR_DNS_FAIL: i64 = 10 // retryable |
| 110 | const NX_RETRY_ERROR_NETWORK_OTHER: i64 = 11 // retryable conservatively |
| 164 | const NX_RETRY_POLICY_FIELDS: i64 = 14 |
| 165 | const NX_RETRY_POLICY_BYTES: i64 = NX_RETRY_POLICY_FIELDS * 8 |
| 169 | const NX_RETRY_DEFAULT_BASE_WAIT_MS: i64 = 250 |
| 170 | const NX_RETRY_DEFAULT_MAX_WAIT_SEC: i64 = 60 |
| 171 | const NX_RETRY_DEFAULT_MAX_ATTEMPTS: i64 = 7 |
| 200 | const NX_RETRY_MS_PER_SEC: i64 = 1000 |
| 203 | const NX_RETRY_SHIFT_CAP: i64 = 30 |
functions
| 85 | func nx_retry_verdict_name(v: i64) -> *u8 |
| 112 | func nx_retry_error_class_name(c: i64) -> *u8 |
| 127 | func nx_retry_error_is_retryable(c: i64) -> i64 called by 1: nx_retry_policy_decide |
| 175 | func nx_retry_policy_new(descriptor_hk: i64) -> *RetryPolicy |
| 214 | func nx_retry_seed(p: *RetryPolicy, now_unix: i64) -> i64 |
| 221 | func nx_retry_compute_wait_ms(p: *RetryPolicy, seed: i64) -> i64 |
| 236 | func nx_retry_compute_wait_seconds(p: *RetryPolicy, seed: i64) -> i64 calls 1: nx_retry_compute_wait_ms |
| 247 | func nx_retry_policy_decide( |
| 286 | func nx_retry_policy_record_success(p: *RetryPolicy) -> i64 |
| 309 | func nx_retry_apply_retry_after(p: *RetryPolicy, retry_after_seconds: i64, seed: i64) -> i64 |