Skip to content

🚀[Feature]: Standardize resilience & retry for Integration (API) module transport #99

Description

Description

Standardize a built-in, configurable resilience/retry mechanism for Integration (API) modules, owned by the transport layer, with smart defaults so the common path needs zero configuration.

Today every Integration (API) module (e.g. GitHub, OpenAI, Anthropic, Domeneshop) is free to handle — or ignore — transient API failures its own way. That means rate limits (429), service-unavailable / 5xx, and network blips are handled inconsistently or not at all, and any retry logic that does exist tends to be reimplemented per function. We should make resilience a default property of the API transport layer, not a per-function afterthought.

The rule this issue proposes to document:

An Integration (API) module's transport helper is the single choke point for resilience. It MUST implement retry with backoff for transient failures, be fully configurable, and ship with smart, common-sense defaults. Public commands (Get-GitHubRepository, etc.) inherit this automatically and can override it per call — but require no configuration to get sane behaviour.

Where this belongs

  • Primary: src/docs/Modules/Module-Types.mdIntegration (API) modules → a new Resilience / retry subsection next to Transport abstraction (the transport helper already owns Invoke-RestMethod/GraphQL calls, so it's the natural owner of retry).
  • Reinforces: src/docs/Modules/Standards.mdParameter design → Smart defaults (the config must be smart-by-default) and the Dependency Inversion rule (public functions depend on a resolved transport abstraction that owns the concrete calls).
  • Complements: 🚀[Feature]: Guidelines for PowerShell-wrapped REST methods #4 (Guidelines for PowerShell-wrapped REST methods) — that issue maps verbs↔methods; this one defines how those calls behave under failure.
  • Relates to: the existing PSModule/Retry module (see Mechanism below).

Proposed behaviour

Retry/backoff lives in the private transport helper (e.g. Invoke-GitHubAPI), not scattered across public functions. It exposes configuration knobs, each with a sensible default, and public commands pass overrides through (default = inherit).

Configuration surface (smart defaults)

Knob Purpose Suggested default
MaxRetries Max retry attempts after the first try 3
Backoff strategy How delay grows between attempts Exponential + jitter
Base delay Starting delay for backoff 1s (cap growth, e.g. max 30s)
Max elapsed / total timeout Upper bound on total time spent retrying ~100s (bounded)
Retriable status codes Which HTTP codes trigger a retry 408, 429, 500, 502, 503, 504
Honour server timing Respect Retry-After and rate-limit reset headers (e.g. x-ratelimit-reset) when present, overriding computed backoff on
Retriable exceptions Transient network conditions (timeouts, connection reset, DNS) timeouts + connection failures
Success vs. error classification What counts as success vs. a retriable/terminal error; allow mapping some codes to a non-error result (e.g. 404 → $null) instead of throwing 2xx = success; configurable

Requirements:

  • Smart by default. The common call path needs no retry parameters at all and still gets rate-limit + 5xx resilience.
  • Configurable from the caller. Public commands expose optional overrides (e.g. a single -Retry config object, or discrete -MaxRetries / -RetryOn params — following object-first parameters + smart defaults from Standards.md) that flow through to the transport helper. Omitting them inherits module defaults.
  • Module-level defaults. Sensible defaults live with the module (transport helper / module settings via Context) so a whole module can tune behaviour once.
  • Observability. Retries should be visible via Write-Verbose/Write-Debug (attempt number, delay, reason) per the messaging standard.
  • Idempotency-aware. Guidance on retrying non-idempotent methods (POST) safely — default retry-on-429/503 is safe; be explicit about when broader retries apply.

Example (illustrative)

# Default: retries transient failures (429/5xx) with backoff + jitter, honours Retry-After. Zero config.
Get-GitHubRepository -Owner 'PSModule' -Name 'docs'

# Caller override: more attempts, also retry 403 secondary-rate-limit, cap total time.
Get-GitHubRepository -Owner 'PSModule' -Name 'docs' -Retry @{
    MaxRetries    = 6
    RetryOn       = 403, 429, 500, 502, 503, 504
    MaxElapsed    = '2m'
}

Mechanism: evolve PSModule/Retry or define the contract

The org already has PSModule/Retry (Invoke-Retry / alias Retry), but it is currently basic and not sufficient as-is:

  • Fixed -Delay only — no exponential backoff, no jitter.
  • Retries on any error — no HTTP status-code awareness.
  • No Retry-After / rate-limit reset handling.
  • No success/error classification (can't treat 404 as $null, can't scope which failures are retriable).

Two options to document/decide:

  1. Extend PSModule/Retry to support status-aware retry, exponential backoff + jitter, Retry-After, and success/error predicates — then make it the standard mechanism every Integration module's transport helper uses.
  2. Define the resilience contract in the standard and let each transport helper implement it (optionally on top of Retry).

Recommendation: option 1 — one shared, well-tested mechanism, adopted by convention.


Acceptance criteria

  • Module-Types.md gains a Resilience / retry subsection under Integration (API) modules describing: transport helper ownership, the default behaviour, the configuration surface + defaults table, and how callers override.
  • Standards.md cross-references it from Smart defaults / Transport abstraction.
  • The retriable-status / backoff / Retry-After / success-classification defaults are stated explicitly.
  • Guidance on caller-side overrides (object-first -Retry config vs. discrete params) and module-level defaults via Context.
  • Decision recorded on evolving PSModule/Retry vs. per-module implementation, with follow-up issue on PSModule/Retry if option 1.
  • Cross-links to 🚀[Feature]: Guidelines for PowerShell-wrapped REST methods #4 and PSModule/Retry.

References

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions