Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
flights: parallelize the 3 provider fetches per search (~3x faster; fixes slow return leg)
Browse files- app/flights_provider.py +14 -3
app/flights_provider.py
CHANGED
|
@@ -24,6 +24,7 @@ Environment (never hardcoded):
|
|
| 24 |
"""
|
| 25 |
|
| 26 |
import os
|
|
|
|
| 27 |
from datetime import datetime, date as _date, timedelta
|
| 28 |
from typing import List, Optional, Dict
|
| 29 |
|
|
@@ -250,13 +251,23 @@ def _search_once(src, dst, date, nonstop, airlines, max_price,
|
|
| 250 |
# search_by_price_range is one-way only; use it just for one-way sweeps.
|
| 251 |
if not return_date:
|
| 252 |
fetchers.insert(1, lambda: _fetch_price_range(src, dst, date, nonstop, max_results, max_price))
|
| 253 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 254 |
try:
|
| 255 |
-
|
| 256 |
except Exception:
|
| 257 |
# a single endpoint failing (rate limit, empty cache) must not sink
|
| 258 |
# the whole search; we still return whatever the others gave.
|
| 259 |
-
|
|
|
|
|
|
|
|
|
|
| 260 |
|
| 261 |
flights = [_normalize(r, date) for r in raw if r.get("price")]
|
| 262 |
flights = _dedupe(flights)
|
|
|
|
| 24 |
"""
|
| 25 |
|
| 26 |
import os
|
| 27 |
+
from concurrent.futures import ThreadPoolExecutor
|
| 28 |
from datetime import datetime, date as _date, timedelta
|
| 29 |
from typing import List, Optional, Dict
|
| 30 |
|
|
|
|
| 251 |
# search_by_price_range is one-way only; use it just for one-way sweeps.
|
| 252 |
if not return_date:
|
| 253 |
fetchers.insert(1, lambda: _fetch_price_range(src, dst, date, nonstop, max_results, max_price))
|
| 254 |
+
# The three endpoints are INDEPENDENT Travelpayouts I/O calls. Running them
|
| 255 |
+
# one after another made a single search ~3x slower than the slowest call -
|
| 256 |
+
# and on a round trip, where the return leg is searched alongside the
|
| 257 |
+
# outbound, that extra latency is exactly why "the departure came but the
|
| 258 |
+
# return didn't load, it takes time". Fan them out on threads and gather;
|
| 259 |
+
# each still fails independently (a rate-limited/empty endpoint returns []),
|
| 260 |
+
# and ThreadPoolExecutor.map preserves order, so dedupe/rank are unchanged.
|
| 261 |
+
def _safe(fetch):
|
| 262 |
try:
|
| 263 |
+
return fetch()
|
| 264 |
except Exception:
|
| 265 |
# a single endpoint failing (rate limit, empty cache) must not sink
|
| 266 |
# the whole search; we still return whatever the others gave.
|
| 267 |
+
return []
|
| 268 |
+
with ThreadPoolExecutor(max_workers=len(fetchers)) as ex:
|
| 269 |
+
for part in ex.map(_safe, fetchers):
|
| 270 |
+
raw.extend(part)
|
| 271 |
|
| 272 |
flights = [_normalize(r, date) for r in raw if r.get("price")]
|
| 273 |
flights = _dedupe(flights)
|