[ka6] fix connecteur jci: 500 transitoire de l'API Workday (jci.wd5, Johnson Controls) — le sync de 05:26 a échoué sur un unique HTTP 500 de POST /wday/cxs/jci/JCI/jobs (endpoint revenu à la normale: rejeu = 3 offres QC actives, médiane historique 3.0, aucune donnée perdue). Cause d'aggravation: _ka_resilient_post (base.py) ne relançait que sur timeout/coupure réseau, donc un seul 5xx passager avortait tout le sync de la source et la marquait stale. Durcissement minimal: relance (3 tentatives, backoff 5s/10s) aussi sur HTTPError >= 500, en direct (chaîne proxy GET-only); 4xx jamais relancés (403/429 anti-bot: rejouer à l'identique ne débloque pas). Testé: simulation 500-500-200 -> succès à la 3e tentative, 404 -> échec immédiat sans relance, 500 persistant -> lève après 3; sync réel jci avec le fix: found=3, 3 actives. pm2 restart job-ka-sync OK, site 200.
1 changed file +14 −6
modified
jobka/connectors/base.py
+14 −6
@@ -5,7 +5,7 @@ | |||||
| 5 | # Fichier : jobka/connectors/base.py | 5 | # Fichier : jobka/connectors/base.py | ||
| 6 | # Rôle : Classe de base des connecteurs + backends de fetch (requests | 6 | # Rôle : Classe de base des connecteurs + backends de fetch (requests | ||
| 7 | # direct, Scrapfly pour les pages carrières JS / derrière anti-bot) | 7 | # direct, Scrapfly pour les pages carrières JS / derrière anti-bot) | ||
| 8 | −# Créé : 2026-08-17 Modifié : 2026-08-26 | 8 | +# Créé : 2026-08-17 Modifié : 2026-08-27 | ||
| 9 | # ============================================================================= | 9 | # ============================================================================= | ||
| 10 | from __future__ import annotations | 10 | from __future__ import annotations | ||
| 11 | 11 | ||||
@@ -239,17 +239,25 @@ if not getattr(BaseConnector, "_KA_RESILIENT_WRAPPED", False): | |||||
| 239 | _ka_orig_post = BaseConnector.post | 239 | _ka_orig_post = BaseConnector.post | ||
| 240 | 240 | ||||
| 241 | def _ka_resilient_post(self, url, **kw): | 241 | def _ka_resilient_post(self, url, **kw): | ||
| 242 | − """POST avec relances : un timeout/coupure ponctuel (ex. Workday lent | 242 | + """POST avec relances : un timeout/coupure ponctuel ou un 5xx passager | ||
| 243 | − sur l'API liste) ne doit pas avorter tout le sync de la source — la | 243 | + (ex. Workday lent ou en erreur interne sur l'API liste — vu sur jci le | ||
| 244 | − chaîne d'escalade proxy est GET-only, on relance donc en direct.""" | 244 | + 2026-08-27, 500 unique puis retour à la normale) ne doit pas avorter | ||
| 245 | + tout le sync de la source — la chaîne d'escalade proxy est GET-only, | ||||
| 246 | + on relance donc en direct. Les 4xx (dont 403/429 anti-bot) ne sont pas | ||||
| 247 | + relancés : rejouer à l'identique ne les débloque pas.""" | ||||
| 245 | last_exc = None | 248 | last_exc = None | ||
| 246 | for attempt in range(3): | 249 | for attempt in range(3): | ||
| 247 | try: | 250 | try: | ||
| 248 | return _ka_orig_post(self, url, **kw) | 251 | return _ka_orig_post(self, url, **kw) | ||
| 249 | except (_ka_requests.ConnectionError, _ka_requests.Timeout) as exc: | 252 | except (_ka_requests.ConnectionError, _ka_requests.Timeout) as exc: | ||
| 250 | last_exc = exc | 253 | last_exc = exc | ||
| 251 | − if attempt < 2: | 254 | + except _ka_requests.HTTPError as exc: | ||
| 252 | − time.sleep(5 * (attempt + 1)) | 255 | + r = getattr(exc, "response", None) | ||
| 256 | + if r is None or r.status_code < 500: | ||||
| 257 | + raise | ||||
| 258 | + last_exc = exc | ||||
| 259 | + if attempt < 2: | ||||
| 260 | + time.sleep(5 * (attempt + 1)) | ||||
| 253 | raise last_exc | 261 | raise last_exc | ||
| 254 | 262 | ||||
| 255 | BaseConnector.get = _ka_resilient_get | 263 | BaseConnector.get = _ka_resilient_get | ||
| 256 | 264 | ||||