Skip to content

fix: respect Shopware Store API 429s during catalog sync - #789

Merged
shyim merged 1 commit into
mainfrom
cursor/store-api-rate-limit-21e8
Aug 11, 2026
Merged

fix: respect Shopware Store API 429s during catalog sync#789
shyim merged 1 commit into
mainfrom
cursor/store-api-rate-limit-21e8

Conversation

@shyim

@shyim shyim commented Aug 11, 2026

Copy link
Copy Markdown
Member

Root cause

Production shopmon-worker was bursting api.shopware.com during store-extension catalog sync:

  1. No 429 handling in shopwareaccount.PluginsByName — any non-200 became an immediate error (store api returned status 429) with no Retry-After / backoff.
  2. Probe fan-out in catalog/sync: every distinct in-use Shopware version was probed, and within each version en_GB + de_DE ran concurrently. On probe failure the sync logged a warn and continued to the next version, so a rate-limit did not stop the stomping — scrapes then redispatched sync for missing compatibility rows.

Changes

Store client (api/internal/shopwareaccount)

  • Shared do() retries HTTP 429 only (other statuses are not blindly retried).
  • Honors Retry-After (delta-seconds or HTTP-date), capped; otherwise exponential backoff with jitter.
  • Caps attempts (default 5) and surfaces APIError / IsRateLimited when still failing.
  • Unit tests cover Retry-After, exponential backoff, exhaustion, and non-429 no-retry.

Catalog sync (api/internal/catalog/sync)

  • Locale probes are serialized (en then de) instead of concurrent.
  • On rate-limit after client retries: abort remaining version probes.
  • Persist any successful probes, but do not advance sync bookkeeping; return a wrapped rate-limit error so the queue / next scrape can retry without marking names fresh.
  • Tests for serialization, abort-after-429 (partial progress), and all-probes-429.

Verification

  • mise run lint — passed (API golangci-lint + frontend oxlint/tsc/oxfmt/vitest)
  • mise run test — passed (go test ./internal/...)
  • Also: go test ./internal/shopwareaccount/ ./internal/catalog/sync/
Open in Web Open in Cursor 

Catalog sync was probing every in-use Shopware version with concurrent
en/de locale calls and continuing after failures, which amplified 429
bursts from api.shopware.com. Add Retry-After/backoff on store client
GETs, serialize locale probes, and abort remaining versions when rate
limited so the job can retry without stomping the API.

Co-authored-by: Soner <github@shyim.de>
@shyim
shyim marked this pull request as ready for review August 11, 2026 20:04
@shyim
shyim merged commit 0a826fe into main Aug 11, 2026
6 checks passed
@shyim
shyim deleted the cursor/store-api-rate-limit-21e8 branch August 11, 2026 20:04
@greptile-apps

greptile-apps Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds bounded 429 retries to the Shopware Store client and serializes catalog locale/version probes so rate limiting stops further fan-out.

  • Parses and caps Retry-After values, with exponential jittered fallback.
  • Aborts remaining catalog probes after an exhausted 429 while retaining completed-version progress.
  • Adds focused retry, serialization, and partial-sync tests.

Confidence Score: 4/5

The PR should not merge until a second-locale 429 preserves the first locale’s successful probe as partial progress.

The new de_DE rate-limit branch explicitly returns nil for the already-fetched en_GB result, preventing the partial persistence promised by the sync behavior and forcing redundant full retries.

Files Needing Attention: api/internal/catalog/sync/service.go, api/internal/catalog/sync/service_test.go

Important Files Changed

Filename Overview
api/internal/catalog/sync/service.go Serializes probes and aborts on rate limiting, but discards a successful English probe when the following German probe returns 429.
api/internal/shopwareaccount/retry.go Adds bounded 429-only retries with Retry-After parsing, capped delays, jitter, response draining, and context-aware sleep.
api/internal/shopwareaccount/store.go Routes PluginsByName through the retry helper and exposes non-success statuses as typed APIError values.
api/internal/catalog/sync/service_test.go Covers serialized requests and version-level aborts, but not an en_GB success followed by a de_DE 429.
api/internal/shopwareaccount/retry_test.go Covers Retry-After, fallback backoff, retry exhaustion, parsing, and immediate handling of non-429 responses.

Sequence Diagram

sequenceDiagram
  participant Sync as Catalog Sync
  participant Store as Shopware Store API
  participant DB as PostgreSQL
  Sync->>Store: Probe en_GB
  Store-->>Sync: 200 catalog data
  Sync->>Store: Probe de_DE
  Store-->>Sync: 429 after retries
  Note over Sync: Current branch discards en_GB result
  Sync-->>Sync: Abort remaining versions
  Note over DB: No partial data persisted when this is the first version
Loading

Fix All in Codex

Reviews (1): Last reviewed commit: "fix: respect Store API 429s during catal..." | Re-trigger Greptile

Comment thread api/internal/catalog/sync/service.go
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants