fix: retry transient Tencent quote failures

This commit is contained in:
王鹏
2026-07-08 22:09:17 +08:00
parent f7ef52ef64
commit 1e8858ac04
2 changed files with 34 additions and 4 deletions

View File

@@ -50,10 +50,12 @@ class TencentQuoteProvider:
*, *,
endpoint: str = TENCENT_ENDPOINT, endpoint: str = TENCENT_ENDPOINT,
timeout: float = 5.0, timeout: float = 5.0,
retries: int = 1,
fetcher: Callable[[str, float], bytes] | None = None, fetcher: Callable[[str, float], bytes] | None = None,
): ):
self.endpoint = endpoint self.endpoint = endpoint
self.timeout = timeout self.timeout = timeout
self.retries = retries
self._fetcher = fetcher or _default_fetcher self._fetcher = fetcher or _default_fetcher
def fetch_quotes(self, instruments: Iterable[Instrument]) -> dict[str, QuoteSnapshot]: def fetch_quotes(self, instruments: Iterable[Instrument]) -> dict[str, QuoteSnapshot]:
@@ -62,12 +64,18 @@ class TencentQuoteProvider:
return {} return {}
query = ",".join(symbols) query = ",".join(symbols)
url = f"{self.endpoint}{query}" url = f"{self.endpoint}{query}"
try: raw = self._fetch_with_retry(url)
raw = self._fetcher(url, self.timeout)
except (OSError, HTTPError, URLError) as exc:
raise QuoteFetchError(f"Tencent quote request failed: {exc}") from exc
return {quote.code: quote for quote in parse_tencent_response(raw)} return {quote.code: quote for quote in parse_tencent_response(raw)}
def _fetch_with_retry(self, url: str) -> bytes:
last_error: Exception | None = None
for _ in range(self.retries + 1):
try:
return self._fetcher(url, self.timeout)
except (OSError, HTTPError, URLError) as exc:
last_error = exc
raise QuoteFetchError(f"Tencent quote request failed: {last_error}") from last_error
def _default_fetcher(url: str, timeout: float) -> bytes: def _default_fetcher(url: str, timeout: float) -> bytes:
with urlopen(url, timeout=timeout) as response: with urlopen(url, timeout=timeout) as response:

View File

@@ -74,3 +74,25 @@ def test_provider_wraps_fetch_errors():
with pytest.raises(QuoteFetchError, match="Tencent quote request failed"): with pytest.raises(QuoteFetchError, match="Tencent quote request failed"):
provider.fetch_quotes([Instrument(id=1, code="000001", name="平安银行")]) provider.fetch_quotes([Instrument(id=1, code="000001", name="平安银行")])
def test_provider_retries_once_after_transient_fetch_error():
raw = (
'v_sz000001="51~平安银行~000001~10.60~10.47~10.44~0~0~0~'
'0~0~0~0~0~0~0~0~0~0~0~0~0~0~0~0~0~0~0~0~~20260708161430~0.13~1.24~10.63~10.34";'
).encode("gbk")
calls = 0
def flaky_fetch(url: str, timeout: float) -> bytes:
nonlocal calls
calls += 1
if calls == 1:
raise OSError("bad gateway")
return raw
provider = TencentQuoteProvider(fetcher=flaky_fetch, retries=1)
quotes = provider.fetch_quotes([Instrument(id=1, code="000001", name="平安银行")])
assert calls == 2
assert quotes["000001"].price == Decimal("10.60")