fix: toten Overpass-Fallback ersetzen + Retry bei 504

- kumi.systems Fallback (tot) durch z.overpass-api.de ersetzt
- Retry-Logik bei transienten HTTP-Fehlern (502/504/429)
- 2s Pause vor Retry, dann naechster Endpunkt
Dieser Commit ist enthalten in:
Claude Dev
2026-03-26 08:28:22 +01:00
Ursprung f4dcd37e78
Commit 1b74c95bac

Datei anzeigen

@@ -1,6 +1,7 @@
"""Overpass Turbo: OverpassQL-Queries gegen OpenStreetMap ausfuehren.""" """Overpass Turbo: OverpassQL-Queries gegen OpenStreetMap ausfuehren."""
import logging import logging
import time import time
import asyncio
import hashlib import hashlib
import httpx import httpx
from fastapi import APIRouter, HTTPException from fastapi import APIRouter, HTTPException
@@ -19,10 +20,13 @@ _CACHE_TTL = 300 # 5 Minuten
_MAX_CACHE_ENTRIES = 50 _MAX_CACHE_ENTRIES = 50
# --- Overpass API --- # --- Overpass API ---
_OVERPASS_URL = "https://overpass-api.de/api/interpreter" _OVERPASS_URLS = [
_OVERPASS_FALLBACK = "https://overpass.kumi.systems/api/interpreter" "https://overpass-api.de/api/interpreter",
"https://z.overpass-api.de/api/interpreter",
]
_MAX_ELEMENTS = 5000 _MAX_ELEMENTS = 5000
_TIMEOUT = 60 _TIMEOUT = 60
_RETRY_CODES = {502, 504, 429} # Transiente Fehler
class OverpassRequest(BaseModel): class OverpassRequest(BaseModel):
@@ -370,20 +374,30 @@ async def execute_query(req: OverpassRequest):
_last_request_time = time.time() _last_request_time = time.time()
# Query ausfuehren # Query ausfuehren (mit Retry bei transienten Fehlern)
data = None data = None
for url in [_OVERPASS_URL, _OVERPASS_FALLBACK]: last_error = None
for url in _OVERPASS_URLS:
for attempt in range(2):
try: try:
async with httpx.AsyncClient(timeout=_TIMEOUT) as client: async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
r = await client.post(url, data={"data": query}) r = await client.post(url, data={"data": query})
if r.status_code in _RETRY_CODES and attempt == 0:
logger.info(f"Overpass {url}: {r.status_code}, retry in 2s...")
await asyncio.sleep(2)
continue
r.raise_for_status() r.raise_for_status()
data = r.json() data = r.json()
break break
except Exception as e: except Exception as e:
logger.warning(f"Overpass {url}: {e}") last_error = f"{url}: {e}"
continue logger.warning(f"Overpass {url} (attempt {attempt+1}): {e}")
break
if data is not None:
break
if data is None: if data is None:
logger.error(f"Alle Overpass-Endpunkte fehlgeschlagen: {last_error}")
raise HTTPException(502, "Overpass API nicht erreichbar") raise HTTPException(502, "Overpass API nicht erreichbar")
elements = data.get("elements", []) elements = data.get("elements", [])