mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 23:47:41 +08:00
fix(tmdb): 空结果缓存使用独立短 TTL,故障恢复后快速自愈,关闭 #6332
- cached 装饰器新增 empty_ttl/empty_if,空结果按独立短 TTL 写入 - TMDB request/async_request/discover 空结果快照 30 分钟过期 - TmdbCache 未识别负缓存(id=0)同步改用 30 分钟短 TTL,正缓存不变
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
from app.runtime.cache import cached
|
||||
from ..tmdb import TMDb
|
||||
from ..tmdb import TMDb, EMPTY_RESULT_CACHE_TTL
|
||||
|
||||
try:
|
||||
from urllib import urlencode
|
||||
@@ -13,7 +13,7 @@ class Discover(TMDb):
|
||||
"tv": "/discover/tv"
|
||||
}
|
||||
|
||||
@cached(maxsize=1, ttl=43200)
|
||||
@cached(maxsize=1, ttl=43200, empty_ttl=EMPTY_RESULT_CACHE_TTL)
|
||||
def discover_movies(self, params_tuple):
|
||||
"""
|
||||
Discover movies by different types of data like average rating, number of votes, genres and certifications.
|
||||
@@ -23,7 +23,7 @@ class Discover(TMDb):
|
||||
params = dict(params_tuple)
|
||||
return self._request_obj(self._urls["movies"], urlencode(params), key="results", call_cached=False)
|
||||
|
||||
@cached(maxsize=1, ttl=43200)
|
||||
@cached(maxsize=1, ttl=43200, empty_ttl=EMPTY_RESULT_CACHE_TTL)
|
||||
def discover_tv_shows(self, params_tuple):
|
||||
"""
|
||||
Discover TV shows by different types of data like average rating, number of votes, genres,
|
||||
@@ -33,7 +33,7 @@ class Discover(TMDb):
|
||||
"""
|
||||
return self._request_obj(self._urls["tv"], urlencode(params_tuple), key="results", call_cached=False)
|
||||
|
||||
@cached(maxsize=1, ttl=43200)
|
||||
@cached(maxsize=1, ttl=43200, empty_ttl=EMPTY_RESULT_CACHE_TTL)
|
||||
async def async_discover_movies(self, params_tuple):
|
||||
"""
|
||||
Discover movies by different types of data like average rating, number of votes, genres and certifications.(异步版本)
|
||||
@@ -43,7 +43,7 @@ class Discover(TMDb):
|
||||
params = dict(params_tuple)
|
||||
return await self._async_request_obj(self._urls["movies"], urlencode(params), key="results", call_cached=False)
|
||||
|
||||
@cached(maxsize=1, ttl=43200)
|
||||
@cached(maxsize=1, ttl=43200, empty_ttl=EMPTY_RESULT_CACHE_TTL)
|
||||
async def async_discover_tv_shows(self, params_tuple):
|
||||
"""
|
||||
Discover TV shows by different types of data like average rating, number of votes, genres,
|
||||
|
||||
@@ -22,6 +22,11 @@ logger = logging.getLogger(__name__)
|
||||
# 故取区间内的经验值。
|
||||
RETRY_BACKOFF_SECONDS = 2
|
||||
|
||||
# 空结果缓存的独立过期时间(秒)。TMDB 代理故障期间「合法 JSON 但 results 为空」的
|
||||
# 响应会随默认 TTL(可达数十小时)固化,故障自愈后同 key 仍持续命中空结果;空结果
|
||||
# 改用 30 分钟短 TTL,既能拦住故障窗口内的重复回源,又能在故障恢复后较快自然失效。
|
||||
EMPTY_RESULT_CACHE_TTL = 30 * 60
|
||||
|
||||
|
||||
def _is_business_failure_snapshot(snapshot) -> bool:
|
||||
"""
|
||||
@@ -37,6 +42,23 @@ def _is_business_failure_snapshot(snapshot) -> bool:
|
||||
return isinstance(json_data, dict) and json_data.get("success") is False
|
||||
|
||||
|
||||
def _is_empty_result_snapshot(snapshot) -> bool:
|
||||
"""
|
||||
判断响应快照是否为空结果(列表/搜索类接口的 results 为空列表)。
|
||||
|
||||
这类快照结构合法但无业务内容,常由代理瞬时故障产生;不能靠 skip_none/skip_empty
|
||||
识别(快照本身是非空字典),需单独谓词判定后按 EMPTY_RESULT_CACHE_TTL 短 TTL
|
||||
缓存。详情类接口无 results 字段,不属于空结果。
|
||||
"""
|
||||
if not isinstance(snapshot, dict):
|
||||
return False
|
||||
json_data = snapshot.get("json")
|
||||
if not isinstance(json_data, dict):
|
||||
return False
|
||||
results = json_data.get("results")
|
||||
return isinstance(results, list) and not results
|
||||
|
||||
|
||||
class TMDb(object):
|
||||
_RESPONSE_SNAPSHOT_MARKER = "__mp_tmdb_response_snapshot__"
|
||||
|
||||
@@ -157,7 +179,8 @@ class TMDb(object):
|
||||
self._wait_on_rate_limit = bool(wait_on_rate_limit)
|
||||
|
||||
@cached(maxsize=settings.CONF.tmdb, ttl=settings.CONF.meta, skip_none=True,
|
||||
skip_if=_is_business_failure_snapshot)
|
||||
skip_if=_is_business_failure_snapshot,
|
||||
empty_ttl=EMPTY_RESULT_CACHE_TTL, empty_if=_is_empty_result_snapshot)
|
||||
def request(self, method, url, data, json, **kwargs):
|
||||
req = self._request_once(method, url, data, json)
|
||||
if req is None and method == "GET" and self._owns_session:
|
||||
@@ -182,7 +205,8 @@ class TMDb(object):
|
||||
return self._req.post_res(url, data=data, json=json)
|
||||
|
||||
@cached(maxsize=settings.CONF.tmdb, ttl=settings.CONF.meta, skip_none=True,
|
||||
skip_if=_is_business_failure_snapshot)
|
||||
skip_if=_is_business_failure_snapshot,
|
||||
empty_ttl=EMPTY_RESULT_CACHE_TTL, empty_if=_is_empty_result_snapshot)
|
||||
async def async_request(self, method, url, data, json, **kwargs):
|
||||
req = await self._async_request_once(method, url, data, json)
|
||||
if req is None:
|
||||
|
||||
Reference in New Issue
Block a user