CDN probe: skip while idle, back off while degraded
The 3-min 64KB video download ran on a timer regardless of whether anyone was using the app — wasteful upstream bandwidth, and pure churn while the CDN 403s our IP. Now: no fetch while no stream has been requested in 15 min (api_stream bumps _LAST_PLAY), and 30-min retry while DEGRADED. Pill state goes stale while idle, which is fine — the next viewer's own request is the true signal, and the probe resumes within a minute of it.
This commit is contained in:
1 parent
0cd79dd701
commit
1154203569
1 file changed
+17
-4
+17
-4
@@ -49,8 +49,15 @@ VERSION = _version()
|
|||||||
# app.js uses the same value as these stats. Change here only.
|
# app.js uses the same value as these stats. Change here only.
|
||||||
DONE_RATIO = 0.95
|
DONE_RATIO = 0.95
|
||||||
|
|
||||||
# latest probe result + last 20 samples (~1h at 3-min spacing), for GET /api/cdn
|
# latest probe result + last 20 samples, for GET /api/cdn
|
||||||
_CDN = {"state": "unknown", "kbps": 0.0, "ts": 0.0, "hist": []}
|
_CDN = {"state": "unknown", "kbps": 0.0, "ts": 0.0, "hist": []}
|
||||||
|
# ponytail: probe only when someone is around to care — skip the 64 KB video
|
||||||
|
# download while the app has been idle (no stream requested in 15 min), and
|
||||||
|
# back off to 30 min while DEGRADED (re-hitting a blocked CDN every 3 min is
|
||||||
|
# just churn). api_stream() bumps _LAST_PLAY.
|
||||||
|
_LAST_PLAY = 0.0
|
||||||
|
_IDLE_SECS = 900
|
||||||
|
_DEGRADED_RETRY = 1800
|
||||||
# origin (primary provider pages/API) health — separate host, separate failure
|
# origin (primary provider pages/API) health — separate host, separate failure
|
||||||
# mode (observed 2026-09-03: CDN fine while origin 503'd "Under Maintenance")
|
# mode (observed 2026-09-03: CDN fine while origin 503'd "Under Maintenance")
|
||||||
_ORIGIN = {"state": "unknown", "msg": "", "ts": 0.0, "hist": []}
|
_ORIGIN = {"state": "unknown", "msg": "", "ts": 0.0, "hist": []}
|
||||||
@@ -60,8 +67,9 @@ _CDN_THRESHOLD = float(os.environ.get("ANIWEB_CDN_KBPS", "40"))
|
|||||||
|
|
||||||
|
|
||||||
def _cdn_probe():
|
def _cdn_probe():
|
||||||
"""Background probe: pull 64KB from the CDN every 3 min and log state
|
"""Background probe: pull 64KB from the CDN and log state flips, so
|
||||||
flips, so server.log shows when a degraded window ends (CDN RECOVERED).
|
server.log shows when a degraded window ends (CDN RECOVERED). Skipped
|
||||||
|
while the app is idle and backed off while DEGRADED (see _LAST_PLAY).
|
||||||
Probe target: ep_id 1 = One Piece E1 on the primary provider.
|
Probe target: ep_id 1 = One Piece E1 on the primary provider.
|
||||||
ponytail: single fixed episode; if it ever disappears, the probe logs
|
ponytail: single fixed episode; if it ever disappears, the probe logs
|
||||||
DEGRADED forever — swap the ID, don't build a picker."""
|
DEGRADED forever — swap the ID, don't build a picker."""
|
||||||
@@ -72,6 +80,9 @@ def _cdn_probe():
|
|||||||
|
|
||||||
state = None
|
state = None
|
||||||
while True:
|
while True:
|
||||||
|
if time.time() - _LAST_PLAY > _IDLE_SECS:
|
||||||
|
time.sleep(60) # nobody's watching: don't burn upstream bandwidth
|
||||||
|
continue
|
||||||
try:
|
try:
|
||||||
m = upstream.master_m3u8(1, "jpn")
|
m = upstream.master_m3u8(1, "jpn")
|
||||||
if not m:
|
if not m:
|
||||||
@@ -97,7 +108,7 @@ def _cdn_probe():
|
|||||||
_CDN["hist"].append({"t": time.time(), "kbps": round(kbps)})
|
_CDN["hist"].append({"t": time.time(), "kbps": round(kbps)})
|
||||||
del _CDN["hist"][:-20]
|
del _CDN["hist"][:-20]
|
||||||
_origin_probe()
|
_origin_probe()
|
||||||
time.sleep(180)
|
time.sleep(_DEGRADED_RETRY if now == "DEGRADED" else 180)
|
||||||
|
|
||||||
|
|
||||||
def _origin_probe():
|
def _origin_probe():
|
||||||
@@ -559,6 +570,8 @@ def api_episodes(anime_id: int):
|
|||||||
|
|
||||||
@app.get("/api/episode/{ep_id}/stream")
|
@app.get("/api/episode/{ep_id}/stream")
|
||||||
def api_stream(ep_id: int, lang: str = "jpn", request: Request = None):
|
def api_stream(ep_id: int, lang: str = "jpn", request: Request = None):
|
||||||
|
global _LAST_PLAY
|
||||||
|
_LAST_PLAY = time.time() # wakes the CDN probe: a viewer is here
|
||||||
def go():
|
def go():
|
||||||
master = _upstream(upstream.master_m3u8, ep_id, lang)
|
master = _upstream(upstream.master_m3u8, ep_id, lang)
|
||||||
if not master:
|
if not master:
|
||||||
|
|||||||
Reference in new issue
Block a user