SPB Git

spb/lou-ka

Public

Lou·Ka — tous les logements à louer du Québec, un seul endroit.

[ka2] fix connecteur groupe_robin: timeouts admin-ajax récurrents retryables sur le POST liste. Au sync de 11:45 le connecteur a échoué (stale) sur un ReadTimeout 30 s — 3e épisode du même hoquet (ConnectTimeout 09-13, ReadTimeout 10-17 et 10-28) sur le POST admin-ajax get_list_immeubles_ajx dont la réponse pèse ~3,9 Mo (backend WP parfois lent), alors que le connecteur est sain (found=62 stable, re-testé live : 200 en 3-5 s ×3). Les deux missions précédentes avaient conclu « transitoire, rien à faire » : le hoquet revient — fix minimal (patron morin ae6ac03) : _post_ajax fait désormais 3 tentatives avec backoff 0/5/10 s sur Timeout/ConnectionError/HTTP 5xx (4xx propagés tels quels), erreur franche inchangée si les 3 échouent (pas de zéro silencieux) ; timeout de classe porté 30 → 60 s pour le payload lourd. Fixtures inchangées, pytest groupe_robin 2 passed. Testé : fetch() venv → 62 unités (715-2 190 $, T-R 39 / St-Hyacinthe 23), run.py sync groupe_robin → found=62 ok (added=0, updated=0, removed=0, 3,5 s) = médiane exacte 62.0, pm2 restart lou-ka-sync, site :8095 → 200.

Simon-Pierre Boucher committed (Oct 28, 2026) parent 8015617 Browse files

1 changed file +31 −12

Unified Split
modified louka/connectors/groupe_robin.py +31 −12
@@ -15,6 +15,7 @@ from __future__ import annotations
15 15 import re
16 16 import time
17 17
18 +import requests
18 19 from bs4 import BeautifulSoup
19 20
20 21 from ..schema import Listing, normalize_unit_type
@@ -64,21 +65,39 @@ class GroupeRobinConnector(BaseConnector):
64 65 source_id = "groupe_robin"
65 66 request_delay = 0.8
66 67 max_pages = 5 # garde-fou (2 pages réelles à 40 immeubles/page)
68 + timeout = 60 # payload admin-ajax ~3,9 Mo, backend WP parfois lent
67 69
70 + # Le POST admin-ajax (3,9 Mo) hoquète par épisodes (ReadTimeout isolés
71 + # 09-13, 10-17, 10-28) alors que le site est sain entre deux — 3
72 + # tentatives avec backoff ; erreur franche inchangée si les 3 échouent.
68 73 def _post_ajax(self, offset: int) -> list:
69 74 """POST admin-ajax paginé, avec la même politesse que get()."""
70 − wait = self.request_delay - (time.time() - self._last_request)
71 − if wait > 0:
72 − time.sleep(wait)
73 − resp = self.session.post(AJAX_URL, data={
74 − "action": "get_list_immeubles_ajx",
75 − "offset": str(offset),
76 − "immeubles_per_page": str(PER_PAGE),
77 − "categories[]": CAT_APPARTEMENT,
78 − }, timeout=self.timeout)
79 − self._last_request = time.time()
80 − resp.raise_for_status()
81 − return resp.json()
75 + last: Exception | None = None
76 + for attempt in range(3):
77 + if attempt:
78 + time.sleep(5 * attempt)
79 + wait = self.request_delay - (time.time() - self._last_request)
80 + if wait > 0:
81 + time.sleep(wait)
82 + try:
83 + resp = self.session.post(AJAX_URL, data={
84 + "action": "get_list_immeubles_ajx",
85 + "offset": str(offset),
86 + "immeubles_per_page": str(PER_PAGE),
87 + "categories[]": CAT_APPARTEMENT,
88 + }, timeout=self.timeout)
89 + except (requests.Timeout, requests.ConnectionError) as exc:
90 + self._last_request = time.time()
91 + last = exc
92 + continue
93 + self._last_request = time.time()
94 + if resp.status_code >= 500:
95 + last = requests.HTTPError(
96 + f"{resp.status_code} sur {AJAX_URL}", response=resp)
97 + continue
98 + resp.raise_for_status()
99 + return resp.json()
100 + raise last # type: ignore[misc]
82 101
83 102 def fetch(self) -> list[Listing]:
84 103 listings: dict[str, Listing] = {}
85 104
Menu

Theme