mirror of
https://github.com/mwisnowski/mtg_python_deckbuilder.git
synced 2025-12-16 23:50:12 +01:00
build(ci): harden preview perf gate startup
This commit is contained in:
parent
2888d97883
commit
0abae06a6e
4 changed files with 57 additions and 9 deletions
|
|
@ -38,14 +38,24 @@ def _fetch_json(url: str) -> Dict[str, Any]:
|
||||||
|
|
||||||
|
|
||||||
def _fetch_json_with_retry(url: str, attempts: int = 3, delay: float = 0.6) -> Dict[str, Any]:
|
def _fetch_json_with_retry(url: str, attempts: int = 3, delay: float = 0.6) -> Dict[str, Any]:
|
||||||
|
last_error: Exception | None = None
|
||||||
for attempt in range(1, attempts + 1):
|
for attempt in range(1, attempts + 1):
|
||||||
try:
|
try:
|
||||||
return _fetch_json(url)
|
return _fetch_json(url)
|
||||||
except Exception: # pragma: no cover - network variability
|
except Exception as exc: # pragma: no cover - network variability
|
||||||
|
last_error = exc
|
||||||
if attempt < attempts:
|
if attempt < attempts:
|
||||||
|
print(json.dumps({ # noqa: T201
|
||||||
|
"event": "preview_perf_fetch_retry",
|
||||||
|
"url": url,
|
||||||
|
"attempt": attempt,
|
||||||
|
"max_attempts": attempts,
|
||||||
|
"error": str(exc),
|
||||||
|
}))
|
||||||
time.sleep(delay * attempt)
|
time.sleep(delay * attempt)
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
raise last_error # pragma: no cover - defensive; should be unreachable
|
||||||
|
|
||||||
|
|
||||||
def select_theme_slugs(base_url: str, count: int) -> List[str]:
|
def select_theme_slugs(base_url: str, count: int) -> List[str]:
|
||||||
|
|
@ -97,12 +107,31 @@ def fetch_all_theme_slugs(base_url: str, page_limit: int = 200) -> List[str]:
|
||||||
slugs: List[str] = []
|
slugs: List[str] = []
|
||||||
offset = 0
|
offset = 0
|
||||||
seen: set[str] = set()
|
seen: set[str] = set()
|
||||||
|
page_attempts = 5
|
||||||
|
page_delay = 1.2
|
||||||
while True:
|
while True:
|
||||||
try:
|
url = f"{base_url.rstrip('/')}/themes/api/themes?limit={page_limit}&offset={offset}"
|
||||||
url = f"{base_url.rstrip('/')}/themes/api/themes?limit={page_limit}&offset={offset}"
|
data: Dict[str, Any] | None = None
|
||||||
data = _fetch_json_with_retry(url)
|
last_error: Exception | None = None
|
||||||
except Exception as e: # pragma: no cover - network variability
|
for attempt in range(1, page_attempts + 1):
|
||||||
raise SystemExit(f"Failed fetching themes page offset={offset}: {e}")
|
try:
|
||||||
|
data = _fetch_json_with_retry(url, attempts=4, delay=0.75)
|
||||||
|
break
|
||||||
|
except Exception as exc: # pragma: no cover - network variability
|
||||||
|
last_error = exc
|
||||||
|
if attempt < page_attempts:
|
||||||
|
print(json.dumps({ # noqa: T201
|
||||||
|
"event": "preview_perf_page_retry",
|
||||||
|
"offset": offset,
|
||||||
|
"attempt": attempt,
|
||||||
|
"max_attempts": page_attempts,
|
||||||
|
"error": str(exc),
|
||||||
|
}))
|
||||||
|
time.sleep(page_delay * attempt)
|
||||||
|
else:
|
||||||
|
raise SystemExit(f"Failed fetching themes page offset={offset}: {exc}")
|
||||||
|
if data is None: # pragma: no cover - defensive
|
||||||
|
raise SystemExit(f"Failed fetching themes page offset={offset}: {last_error}")
|
||||||
items = data.get("items") or []
|
items = data.get("items") or []
|
||||||
for it in items:
|
for it in items:
|
||||||
if not isinstance(it, dict):
|
if not isinstance(it, dict):
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import time
|
||||||
import urllib.error
|
import urllib.error
|
||||||
import urllib.request
|
import urllib.request
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
def _wait_for_service(base_url: str, attempts: int = 8, delay: float = 1.5) -> bool:
|
def _wait_for_service(base_url: str, attempts: int = 12, delay: float = 1.5) -> bool:
|
||||||
health_url = base_url.rstrip("/") + "/healthz"
|
health_url = base_url.rstrip("/") + "/healthz"
|
||||||
last_error: Exception | None = None
|
last_error: Exception | None = None
|
||||||
for attempt in range(1, attempts + 1):
|
for attempt in range(1, attempts + 1):
|
||||||
|
|
@ -40,7 +40,7 @@ def _wait_for_service(base_url: str, attempts: int = 8, delay: float = 1.5) -> b
|
||||||
break
|
break
|
||||||
except Exception as exc: # pragma: no cover - network variability
|
except Exception as exc: # pragma: no cover - network variability
|
||||||
last_error = exc
|
last_error = exc
|
||||||
time.sleep(delay)
|
time.sleep(delay * attempt)
|
||||||
print(json.dumps({
|
print(json.dumps({
|
||||||
"event": "ci_perf_error",
|
"event": "ci_perf_error",
|
||||||
"stage": "startup",
|
"stage": "startup",
|
||||||
|
|
|
||||||
|
|
@ -18,3 +18,22 @@ def test_fetch_all_theme_slugs_retries(monkeypatch):
|
||||||
|
|
||||||
assert slugs == ["alpha"]
|
assert slugs == ["alpha"]
|
||||||
assert calls["count"] == 2
|
assert calls["count"] == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_fetch_all_theme_slugs_page_level_retry(monkeypatch):
|
||||||
|
calls = {"count": 0}
|
||||||
|
|
||||||
|
def fake_fetch_with_retry(url, attempts=3, delay=0.6): # type: ignore[override]
|
||||||
|
calls["count"] += 1
|
||||||
|
if calls["count"] < 3:
|
||||||
|
raise RuntimeError("service warming up")
|
||||||
|
assert url.endswith("offset=0")
|
||||||
|
return {"items": [{"id": "alpha"}], "next_offset": None}
|
||||||
|
|
||||||
|
monkeypatch.setattr(perf, "_fetch_json_with_retry", fake_fetch_with_retry)
|
||||||
|
monkeypatch.setattr(perf.time, "sleep", lambda *_args, **_kwargs: None)
|
||||||
|
|
||||||
|
slugs = perf.fetch_all_theme_slugs("http://example.com", page_limit=1)
|
||||||
|
|
||||||
|
assert slugs == ["alpha"]
|
||||||
|
assert calls["count"] == 3
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ SHOW_SETUP = _as_bool(os.getenv("SHOW_SETUP"), True)
|
||||||
SHOW_DIAGNOSTICS = _as_bool(os.getenv("SHOW_DIAGNOSTICS"), False)
|
SHOW_DIAGNOSTICS = _as_bool(os.getenv("SHOW_DIAGNOSTICS"), False)
|
||||||
SHOW_COMMANDERS = _as_bool(os.getenv("SHOW_COMMANDERS"), True)
|
SHOW_COMMANDERS = _as_bool(os.getenv("SHOW_COMMANDERS"), True)
|
||||||
SHOW_VIRTUALIZE = _as_bool(os.getenv("WEB_VIRTUALIZE"), False)
|
SHOW_VIRTUALIZE = _as_bool(os.getenv("WEB_VIRTUALIZE"), False)
|
||||||
ENABLE_THEMES = _as_bool(os.getenv("ENABLE_THEMES"), False)
|
ENABLE_THEMES = _as_bool(os.getenv("ENABLE_THEMES"), True)
|
||||||
ENABLE_PWA = _as_bool(os.getenv("ENABLE_PWA"), False)
|
ENABLE_PWA = _as_bool(os.getenv("ENABLE_PWA"), False)
|
||||||
ENABLE_PRESETS = _as_bool(os.getenv("ENABLE_PRESETS"), False)
|
ENABLE_PRESETS = _as_bool(os.getenv("ENABLE_PRESETS"), False)
|
||||||
ALLOW_MUST_HAVES = _as_bool(os.getenv("ALLOW_MUST_HAVES"), False)
|
ALLOW_MUST_HAVES = _as_bool(os.getenv("ALLOW_MUST_HAVES"), False)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue