fix(proxy): rozdziel wygasly podpis od awarii CDN + ponow przejsciowe 5xx
Zgloszenie 7f2234eb: "Cos nie tak z ekstraktorem? Bo samo wideo dziala a
aplikacja zglasza problem z hostem" (scena galaxyporn).
Ekstraktor jest SPRAWNY. Sprawdzone end-to-end: resolve 200, master m3u8,
wariant, 525 segmentow, 60/60 pobranych. Telemetria 30 dni dla tego
zrodla: 1310 sukcesow, 25 bledow (1,9%).
Ale przy okazji wyszlo cos wiekszego. Wszystkie te bledy to "502", a
mapowanie w `_upstream_error_response` sklejalo w ten kod DWIE rozne
awarie: upstream 5xx (CDN padl) ORAZ kazde inne 4xx, w tym 403. Test
syntetyczny: upstream oddaje 403, nasze proxy oddawalo 502.
To bolalo podwojnie. `playback_events.error_detail` bierze sie z komunikatu
ExoPlayera, wiec w telemetrii nie dalo sie odroznic "CDN padl" od "wygasl
podpisany token" — a to zupelnie inne naprawy (pierwsze: czekac, drugie:
odswiezyc resolve). Do tego 403 szedl przez raise jako "moze nasz bug" i
zasmiecal Sentry, choc wygasanie tokenow na tych CDN-ach jest NORMALNE.
Zmiany:
1. 401/403 przepuszczane z prawdziwym kodem (+ log INFO, bez wyjatku).
Cache i tak byl juz invalidowany na 401/403/404/410, wiec kolejny
resolve dostaje swiezy URL.
2. Przejsciowe 502/503/504 ponawiane RAZ (tylko GET/HEAD, idempotentne).
Te CDN-y stoja na wielu wezlach (galaxyporn: 3 rozne IP w jednej
sesji), wiec druga proba zwykle trafia w zdrowy. 4xx nie ponawiamy.
Zweryfikowane: 403 wychodzi teraz jako 403 (bylo 502), normalne
odtwarzanie bez zmian (60/60 segmentow 200).
Uwaga: sciezka ponowienia nie zostala odpalona end-to-end, bo 502 nie da
sie wywolac na zadanie. Widac ja w logu ("proxy: upstream N, ponawiam").
This commit is contained in:
parent
dde21f2e6e
commit
1a75433281
1 changed files with 47 additions and 1 deletions
|
|
@ -31,6 +31,7 @@ import time
|
|||
from typing import Annotated
|
||||
from urllib.parse import urljoin, urlparse
|
||||
|
||||
import asyncio
|
||||
import httpx
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Request
|
||||
from fastapi.responses import Response, StreamingResponse
|
||||
|
|
@ -714,6 +715,28 @@ async def proxy_stream(
|
|||
stream=True,
|
||||
follow_redirects=True,
|
||||
)
|
||||
# Przejściowy błąd SERWERA upstreamu → jedno ponowienie, zanim oddamy błąd
|
||||
# do playera. Te CDN-y stoją na wielu węzłach (galaxyporn: 203.188.166.163,
|
||||
# 185.237.106.182, 185.237.107.133), więc druga próba zwykle trafia w zdrowy.
|
||||
#
|
||||
# Telemetria 30 dni: 502/503 to JEDYNA klasa błędów playbacku przy kilku
|
||||
# niezależnych źródłach naraz (porndoe 65, 8kporner 37, galaxyporn 22), przy
|
||||
# 1310 udanych odtworzeniach samego galaxyporn, czyli 1,9%. Zgłoszenie
|
||||
# 7f2234eb („samo wideo działa a aplikacja zgłasza problem z hostem") to
|
||||
# dokładnie ten przypadek: dla usera wygląda na zepsute źródło, a to mignięcie.
|
||||
#
|
||||
# Ponawiamy TYLKO 502/503/504 i tylko dla GET/HEAD (idempotentne, segment HLS
|
||||
# to zwykły GET). 4xx zostawiamy: 403/410 to wygasły token, a nie chwilowa
|
||||
# awaria, i mają własną ścieżkę z invalidacją cache niżej.
|
||||
if upstream.status_code in (502, 503, 504) and method.upper() in ("GET", "HEAD"):
|
||||
await upstream.aclose()
|
||||
await asyncio.sleep(0.25)
|
||||
log.info("proxy: upstream %d, ponawiam %s", upstream.status_code, target[:70])
|
||||
upstream = await client.send(
|
||||
client.build_request(method, target, headers=headers),
|
||||
stream=True,
|
||||
follow_redirects=True,
|
||||
)
|
||||
if upstream.status_code >= 400:
|
||||
status = upstream.status_code
|
||||
ups_headers = dict(upstream.headers)
|
||||
|
|
@ -854,5 +877,28 @@ def _upstream_error_response(
|
|||
headers={"Retry-After": "5"},
|
||||
media_type="text/plain",
|
||||
)
|
||||
# 4xx other (403 itp.), raise żeby Sentry zarejestrował (może bug naszego kodu)
|
||||
if status in (401, 403):
|
||||
# WYGASŁY/NIEWAŻNY PODPIS, nie awaria hosta. Przepuszczamy prawdziwy kod zamiast
|
||||
# mapować na 502, z dwóch powodów:
|
||||
#
|
||||
# 1. Diagnostyka. `playback_events.error_detail` bierze się z komunikatu
|
||||
# ExoPlayera („Response code: 502"), więc dopóki mapowaliśmy wszystko na 502,
|
||||
# w telemetrii nie dało się odróżnić „CDN padł" od „token wygasł". Zgłoszenie
|
||||
# 7f2234eb („samo wideo działa a aplikacja zgłasza problem z hostem") to
|
||||
# dokładnie ta dwuznaczność: dla usera źródło wygląda na zepsute, a wystarczy
|
||||
# świeży resolve.
|
||||
# 2. Sentry. Wygaśnięcie podpisanego URL-a jest NORMALNE (te CDN-y dają tokeny
|
||||
# na minuty), a raise robił z tego wyjątek „może nasz bug" i zaśmiecał alerty.
|
||||
#
|
||||
# Cache i tak jest tu invalidowany wyżej (401/403/404/410), więc kolejny resolve
|
||||
# dostanie świeży URL.
|
||||
log.info("proxy upstream %d (wygasly podpis?) for %s", status, target)
|
||||
return Response(
|
||||
content=f"upstream {status}",
|
||||
status_code=status,
|
||||
media_type="text/plain",
|
||||
headers={"Cache-Control": "no-store"},
|
||||
)
|
||||
# Pozostałe 4xx (400/405/451…) nadal jako 502 + wyjątek: tego NIE spodziewamy się
|
||||
# od poprawnie zbudowanego requestu, więc chcemy o tym wiedzieć w Sentry.
|
||||
raise HTTPException(status_code=502, detail=f"upstream {status}")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue