Problem
Each module that calls an external API (GitLab, Keycloak, SonarQube, Nexus, Argo CD, Vault, Grafana/Observability) handles transient upstream failures ad hoc.
The GitLab subgroup-creation path recently needed a bespoke withTransientRetry inside GitlabClientService to survive GitLab's intermittent HTTP 500 on POST /api/v4/groups (it aborted the whole observability ensureProjectRepository sync on a single blip).
Sibling modules have the same exposure (5xx, 429/rate-limit, timeouts) but no shared handling, so each would reinvent retry/backoff or, worse, fail an entire project sync on one transient error.
Acceptance
References
- Private fix diff:
gitlab-client.service.ts withTransientRetry + 4 tests (commit pending on fix/footer-version-link-404 working tree).
Problem
Each module that calls an external API (GitLab, Keycloak, SonarQube, Nexus, Argo CD, Vault, Grafana/Observability) handles transient upstream failures ad hoc.
The GitLab subgroup-creation path recently needed a bespoke
withTransientRetryinsideGitlabClientServiceto survive GitLab's intermittent HTTP 500 onPOST /api/v4/groups(it aborted the whole observabilityensureProjectRepositorysync on a single blip).Sibling modules have the same exposure (5xx, 429/rate-limit, timeouts) but no shared handling, so each would reinvent retry/backoff or, worse, fail an entire project sync on one transient error.
Acceptance
apps/server-nestjs/src/utilswith configurable attempts/backoff and a transient-status predicate (500/502/503/504/429).gitlab-client.service.tsuses the shared helper instead of its privatewithTransientRetry(verified by grepping forprivate withTransientRetryreturning no match).keycloak,sonarqube,nexus,argocd,vault,observability-clientis grepped for external create/update calls; the helper is applied where a transient 5xx/429 would abort a sync, or an issue comment records the module as exempt.References
gitlab-client.service.tswithTransientRetry+ 4 tests (commit pending onfix/footer-version-link-404working tree).