From 163b942ea4ae667090b93358e606c0ce298f08c9 Mon Sep 17 00:00:00 2001 From: rene Date: Sat, 25 Apr 2026 22:09:50 +0200 Subject: [PATCH] =?UTF-8?q?OSM:=20Fair-Use-Rate-Limit=20+=20User-Agent=20f?= =?UTF-8?q?=C3=BCr=20Overpass-Anfragen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Semaphore 2→1 (nur 1 gleichzeitige Anfrage), 2s Mindestabstand, User-Agent mit App-Name und Kontakt — Standard-Höflichkeit für Community-Dienste wie kumi.systems. --- backend/routes/osm.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/backend/routes/osm.py b/backend/routes/osm.py index 7c1cd58..2c7375b 100644 --- a/backend/routes/osm.py +++ b/backend/routes/osm.py @@ -24,8 +24,11 @@ OVERPASS_URLS = [ 'https://overpass-api.de/api/interpreter', ] -# Globales Limit: max 2 gleichzeitige Overpass-Anfragen (Prewarm + User geteilt) -_overpass_sem = asyncio.Semaphore(2) +# Max 1 gleichzeitige Overpass-Anfrage + 2s Mindestabstand (Fair Use) +_overpass_sem = asyncio.Semaphore(1) +_overpass_last_req = 0.0 +_OVERPASS_MIN_DELAY = 2.0 # Sekunden zwischen Anfragen +_OVERPASS_UA = 'BanYaro/1.0 (https://banyaro.app; dog-walking PWA; contact: mail@motocamp.de)' OSM_QUERIES = { 'waste_basket': '[out:json][timeout:20];node["amenity"="waste_basket"]({bbox});out;', @@ -68,12 +71,22 @@ def _covering_tiles(south, west, north, east, zoom): # Overpass-Fetch + Cache # ------------------------------------------------------------------ async def _fetch_overpass(query): + global _overpass_last_req for url in OVERPASS_URLS: for attempt in range(2): try: async with _overpass_sem: - async with httpx.AsyncClient(timeout=40) as client: + # Fair-Use: Mindestabstand zwischen Anfragen einhalten + import time + wait = _OVERPASS_MIN_DELAY - (time.monotonic() - _overpass_last_req) + if wait > 0: + await asyncio.sleep(wait) + async with httpx.AsyncClient( + timeout=40, + headers={'User-Agent': _OVERPASS_UA}, + ) as client: r = await client.post(url, data={'data': query}) + _overpass_last_req = time.monotonic() if r.status_code == 200: return r.json().get('elements', []) if r.status_code == 429: