feat: retry catalog sync with exponential backoff to speed up cold-start recovery - #706
feat: retry catalog sync with exponential backoff to speed up cold-start recovery#706stefinie123 wants to merge 2 commits into
Conversation
Signed-off-by: Stefinie Fernando <minolispencer@gmail.com>
Signed-off-by: Stefinie Fernando <minolispencer@gmail.com>
📝 WalkthroughWalkthroughCatalog synchronization now retries failed runs using capped exponential backoff. Client-credentials token acquisition retries transient failures, while catalog API authentication fails fast when token retrieval fails. Retry timing is configurable through ChangesCatalog sync resilience
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related issues
Suggested reviewers: Sequence Diagram(s)sequenceDiagram
participant TaskRunner
participant OpenChoreoEntityProvider
participant OpenChoreoApiClient
participant ClientCredentialsProvider
participant CatalogAPI
TaskRunner->>OpenChoreoEntityProvider: trigger sync tick
OpenChoreoEntityProvider->>OpenChoreoApiClient: run catalog sync
OpenChoreoApiClient->>ClientCredentialsProvider: obtain service token
ClientCredentialsProvider->>CatalogAPI: request token
CatalogAPI-->>ClientCredentialsProvider: token or transient failure
ClientCredentialsProvider-->>OpenChoreoApiClient: token or final error
OpenChoreoApiClient->>CatalogAPI: authenticated catalog request
CatalogAPI-->>OpenChoreoEntityProvider: sync response
OpenChoreoEntityProvider->>TaskRunner: schedule normal or backoff retry
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
plugins/catalog-backend-module-openchoreo/src/provider/OpenChoreoEntityProvider.ts (2)
454-460: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
run()bypasses the adaptive-cadence bookkeeping.A manual/external
run()that succeeds leavesconsecutiveFailuresandnextRetryAtMsuntouched, so the provider keeps behaving as if it were still failing (and a success doesn't shift the normal-cadence window). Consider routingrun()through the same state update, or documenting that it is intentionally out-of-band.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/catalog-backend-module-openchoreo/src/provider/OpenChoreoEntityProvider.ts` around lines 454 - 460, Update OpenChoreoEntityProvider.run() to use the same adaptive-cadence success bookkeeping as the normal execution path, resetting consecutiveFailures and updating nextRetryAtMs after a successful runNew() call. Preserve the existing connection validation and ensure failures continue to propagate without being recorded as successes.
160-169: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winClamp the configured intervals to sane positives.
A misconfigured
retryInterval: 0(or a negative value) makesbackoffMs0 and turns every tick into a full sync;frequency: 0does the same via theMath.mincap. A floor here is cheap insurance.♻️ Proposed clamp
- this.syncFrequencyMs = - (config.getOptionalNumber('openchoreo.schedule.frequency') ?? 300) * 1000; + this.syncFrequencyMs = + Math.max( + 1, + config.getOptionalNumber('openchoreo.schedule.frequency') ?? 300, + ) * 1000; @@ - this.retryBaseMs = - (config.getOptionalNumber('openchoreo.schedule.retryInterval') ?? 30) * - 1000; + this.retryBaseMs = + Math.max( + 1, + config.getOptionalNumber('openchoreo.schedule.retryInterval') ?? 30, + ) * 1000;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/catalog-backend-module-openchoreo/src/provider/OpenChoreoEntityProvider.ts` around lines 160 - 169, Clamp the configured values used to initialize syncFrequencyMs and retryBaseMs to a sane positive minimum, after applying their defaults and converting to milliseconds. Update the initialization near OpenChoreoEntityProvider’s syncFrequencyMs and retryBaseMs assignments so zero or negative schedule.frequency and schedule.retryInterval values cannot produce zero-delay backoff or caps.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/openchoreo-auth/src/ClientCredentialsProvider.ts`:
- Around line 148-177: Update both expires_in fallback branches in the token
expiry logic surrounding decodeJwt and expiresAt to validate that
tokenResponse.expires_in is numeric before calculating the expiration. When it
is missing or non-numeric and no usable JWT exp exists, use a finite default
expiry so isTokenValid() can continue reusing the cached token instead of
producing NaN.
---
Nitpick comments:
In
`@plugins/catalog-backend-module-openchoreo/src/provider/OpenChoreoEntityProvider.ts`:
- Around line 454-460: Update OpenChoreoEntityProvider.run() to use the same
adaptive-cadence success bookkeeping as the normal execution path, resetting
consecutiveFailures and updating nextRetryAtMs after a successful runNew() call.
Preserve the existing connection validation and ensure failures continue to
propagate without being recorded as successes.
- Around line 160-169: Clamp the configured values used to initialize
syncFrequencyMs and retryBaseMs to a sane positive minimum, after applying their
defaults and converting to milliseconds. Update the initialization near
OpenChoreoEntityProvider’s syncFrequencyMs and retryBaseMs assignments so zero
or negative schedule.frequency and schedule.retryInterval values cannot produce
zero-delay backoff or caps.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 3cba3b6d-73aa-44b4-9875-f8ceac568725
📒 Files selected for processing (10)
.changeset/catalog-sync-retry-backoff.mdapp-config.production.yamlapp-config.yamlpackages/openchoreo-auth/src/ClientCredentialsProvider.test.tspackages/openchoreo-auth/src/ClientCredentialsProvider.tsplugins/catalog-backend-module-openchoreo/src/module.tsplugins/catalog-backend-module-openchoreo/src/provider/OpenChoreoEntityProvider.test.tsplugins/catalog-backend-module-openchoreo/src/provider/OpenChoreoEntityProvider.tsplugins/catalog-backend-module-openchoreo/src/utils/openChoreoApiClient.tsplugins/openchoreo-common/config.d.ts
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
|
Closing this as we decided not to move forward with this PR , since in memory state for keeping track of failed syncs will not function correctly on a multi replica scenario. Additionally we decided not do any custom implementations for failure scenario since backstage catalog sync has no in built retry fast on failure (since it is designed to be eventually consistent) , so building a custom retry + state mechanism would go against the framework's grain and feels like a hack. |
Purpose
Resolves openchoreo/openchoreo#4324
After a cluster cold-start (
k3d cluster stop/start), coreDNS and the Thunder IdP take ~1–2 min to become reachable. During that window the catalog sync can't get a service token, its first API call fails, and the whole periodic sync aborts —leaving the Backstage catalog empty/partial until the next scheduled run (default 5 min), long after the backend has recovered.This PR makes the sync recover within ~30s of the backend becoming reachable instead of waiting a full sync interval.
Goals
Approach
retryInterval(new config, default 30s) and the provider throttles internally: a healthy sync still runs only everyschedule.frequency, but a failed run retries withcapped exponential backoff — starting at
retryInterval, doubling per consecutive failure, capped atfrequency. It never stops retrying (so the catalog self-heals whenever the backend returns) and backs off during a sustained outageinstead of hammering.
ClientCredentialsProviderretries transient token failures (network errors / 5xx) with backoff; 4xx (e.g. bad credentials) fail immediately.openchoreo.schedule.retryInterval(default 30s) to the config schema and app-config; env-driven in production (OPENCHOREO_CATALOG_SYNC_RETRY_INTERVAL), matching howfrequencyis handled.User stories
Release note
Documentation
Training
Certification
Marketing
Automation tests
Security checks
Samples
Related PRs
Migrations (if applicable)
Test environment
Learning
Summary by CodeRabbit