You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.md → Integration (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.md → Parameter 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).
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
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=6RetryOn=403,429,500,502,503,504MaxElapsed='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:
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.
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.
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:
Where this belongs
src/docs/Modules/Module-Types.md→ Integration (API) modules → a new Resilience / retry subsection next to Transport abstraction (the transport helper already ownsInvoke-RestMethod/GraphQL calls, so it's the natural owner of retry).src/docs/Modules/Standards.md→ Parameter 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).PSModule/Retrymodule (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)
MaxRetries31s(cap growth, e.g. max30s)~100s(bounded)408, 429, 500, 502, 503, 504Retry-Afterand rate-limit reset headers (e.g.x-ratelimit-reset) when present, overriding computed backoffon404 → $null) instead of throwingRequirements:
5xxresilience.-Retryconfig object, or discrete-MaxRetries/-RetryOnparams — following object-first parameters + smart defaults fromStandards.md) that flow through to the transport helper. Omitting them inherits module defaults.Context) so a whole module can tune behaviour once.Write-Verbose/Write-Debug(attempt number, delay, reason) per the messaging standard.POST) safely — default retry-on-429/503is safe; be explicit about when broader retries apply.Example (illustrative)
Mechanism: evolve
PSModule/Retryor define the contractThe org already has
PSModule/Retry(Invoke-Retry/ aliasRetry), but it is currently basic and not sufficient as-is:-Delayonly — no exponential backoff, no jitter.Retry-After/ rate-limit reset handling.404as$null, can't scope which failures are retriable).Two options to document/decide:
PSModule/Retryto 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.Retry).Recommendation: option 1 — one shared, well-tested mechanism, adopted by convention.
Acceptance criteria
Module-Types.mdgains 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.mdcross-references it from Smart defaults / Transport abstraction.Retry-After/ success-classification defaults are stated explicitly.-Retryconfig vs. discrete params) and module-level defaults viaContext.PSModule/Retryvs. per-module implementation, with follow-up issue onPSModule/Retryif option 1.PSModule/Retry.References
src/docs/Modules/Module-Types.md— Integration (API) modules → Transport abstractionsrc/docs/Modules/Standards.md— Parameter design → Smart defaults; SOLID → Dependency InversionPSModule/Retry