fix: ignore expected module rate limits

This commit is contained in:
jxxghp
2026-05-20 09:38:37 +08:00
parent 6edb627145
commit 71dc9df7ff
2 changed files with 124 additions and 0 deletions

View File

@@ -26,6 +26,7 @@ from app.helper.message import MessageHelper, MessageQueueManager, MessageTempla
from app.helper.service import ServiceConfigHelper
from app.log import logger
from app.schemas import (
RateLimitExceededException,
TransferInfo,
TransferTorrent,
ExistMediaInfo,
@@ -204,6 +205,18 @@ class ChainBase(metaclass=ABCMeta):
},
)
@staticmethod
def __handle_rate_limit_error(
err: RateLimitExceededException, source_type: str, source_id: str,
method: str, **kwargs
) -> None:
"""
处理本地限流跳过,避免预期的限流状态进入系统错误告警。
"""
if kwargs.get("raise_exception"):
raise err
logger.info(f"{source_type} {source_id}.{method} 已限流,跳过执行:{str(err)}")
def __execute_plugin_modules(
self, method: str, result: Any, *args, **kwargs
) -> Any:
@@ -227,6 +240,10 @@ class ChainBase(metaclass=ABCMeta):
result.extend(temp)
else:
break
except RateLimitExceededException as err:
self.__handle_rate_limit_error(
err, "插件", plugin_id, method, **kwargs
)
except Exception as err:
self.__handle_plugin_error(
err, plugin_id, plugin_name, method, **kwargs
@@ -264,6 +281,10 @@ class ChainBase(metaclass=ABCMeta):
result.extend(temp)
else:
break
except RateLimitExceededException as err:
self.__handle_rate_limit_error(
err, "插件", plugin_id, method, **kwargs
)
except Exception as err:
self.__handle_plugin_error(
err, plugin_id, plugin_name, method, **kwargs
@@ -303,6 +324,10 @@ class ChainBase(metaclass=ABCMeta):
else:
# 中止继续执行
break
except RateLimitExceededException as err:
self.__handle_rate_limit_error(
err, "模块", module_id, method, **kwargs
)
except Exception as err:
logger.error(traceback.format_exc())
self.__handle_system_error(
@@ -353,6 +378,10 @@ class ChainBase(metaclass=ABCMeta):
else:
# 中止继续执行
break
except RateLimitExceededException as err:
self.__handle_rate_limit_error(
err, "模块", module_id, method, **kwargs
)
except Exception as err:
logger.error(traceback.format_exc())
self.__handle_system_error(

View File

@@ -0,0 +1,95 @@
import asyncio
import sys
import unittest
from types import ModuleType
from unittest.mock import Mock
sys.modules.setdefault("qbittorrentapi", ModuleType("qbittorrentapi"))
setattr(sys.modules["qbittorrentapi"], "TorrentFilesList", list)
sys.modules.setdefault("transmission_rpc", ModuleType("transmission_rpc"))
setattr(sys.modules["transmission_rpc"], "File", object)
from app.chain import ChainBase
from app.schemas import RateLimitExceededException
class _LimitedModule:
def get_name(self):
"""
返回测试模块名称。
"""
return "限流测试模块"
def get_priority(self):
"""
返回测试模块优先级。
"""
return 1
def limited_method(self, raise_exception: bool = False):
"""
模拟同步模块在本地限流期间跳过调用。
"""
raise RateLimitExceededException("[limited_method] 限流期间,跳过调用")
async def async_limited_method(self, raise_exception: bool = False):
"""
模拟异步模块在本地限流期间跳过调用。
"""
raise RateLimitExceededException("[async_limited_method] 限流期间,跳过调用")
class ChainRateLimitTest(unittest.TestCase):
def _build_chain(self):
"""
构造隔离的 ChainBase避免依赖真实模块和插件运行状态。
"""
chain = ChainBase()
limited_module = _LimitedModule()
chain.pluginmanager = Mock()
chain.pluginmanager.get_plugin_modules.return_value = {}
chain.modulemanager = Mock()
chain.modulemanager.get_running_modules.return_value = [limited_module]
chain.messagehelper = Mock()
chain.eventmanager = Mock()
return chain
def test_rate_limit_is_not_reported_as_system_error(self):
"""
本地限流跳过不应写入系统错误通知或事件。
"""
chain = self._build_chain()
result = chain.run_module("limited_method")
self.assertIsNone(result)
chain.messagehelper.put.assert_not_called()
chain.eventmanager.send_event.assert_not_called()
def test_rate_limit_can_still_be_raised_explicitly(self):
"""
调用方显式要求抛出异常时,限流异常应继续向上抛出。
"""
chain = self._build_chain()
with self.assertRaises(RateLimitExceededException):
chain.run_module("limited_method", raise_exception=True)
chain.messagehelper.put.assert_not_called()
chain.eventmanager.send_event.assert_not_called()
def test_async_rate_limit_is_not_reported_as_system_error(self):
"""
异步模块的本地限流跳过也不应触发系统错误路径。
"""
chain = self._build_chain()
result = asyncio.run(chain.async_run_module("async_limited_method"))
self.assertIsNone(result)
chain.messagehelper.put.assert_not_called()
chain.eventmanager.send_event.assert_not_called()
if __name__ == "__main__":
unittest.main()