perf(memory): stop retaining module sources and cap nginx workers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
InfinityPacer
2026-09-28 02:45:53 +08:00
committed by InfinityPacer
parent 9af903e677
commit c924a455b2
3 changed files with 150 additions and 58 deletions

View File

@@ -4,14 +4,48 @@ import importlib
import inspect
import pkgutil
import textwrap
import threading
import tokenize
import weakref
from pathlib import Path
from types import FunctionType
from typing import Any, Callable, List, get_type_hints
from types import CodeType, FunctionType
from typing import Any, Callable, List, Optional, get_type_hints
FilterFuncType = Callable[[str, Any], bool]
# check_method 结果缓存:弱引用代码对象,模块或插件卸载后自动清理
_implemented_cache: "weakref.WeakKeyDictionary[CodeType, bool]" = weakref.WeakKeyDictionary()
_implemented_cache_lock = threading.Lock()
def _read_function_source(code: CodeType) -> str:
"""
读取函数定义源码。
与 inspect.getsource 的定位方式一致,但直接读文件而不经 linecache:
getsource 会把整份源文件留在 linecache 中常驻,所有模块和插件累计约 10MB。
"""
with tokenize.open(code.co_filename) as fh:
lines = fh.readlines()
return "".join(inspect.getblock(lines[code.co_firstlineno - 1:]))
def _is_placeholder_stmt(stmt: ast.stmt) -> bool:
"""pass、docstring、... 与 raise NotImplementedError 视为未实现的占位语句。"""
if isinstance(stmt, ast.Pass):
return True
if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Constant):
return isinstance(stmt.value.value, str) or stmt.value.value is Ellipsis
if isinstance(stmt, ast.Raise):
exc = stmt.exc
if isinstance(exc, ast.Call):
exc = exc.func
return isinstance(exc, ast.Name) and exc.id == "NotImplementedError"
return False
def _default_filter(name: str, obj: Any) -> bool:
"""接受具有名称和值的动态加载对象。"""
return bool(name and obj)
@@ -151,35 +185,32 @@ class ObjectUtils:
def check_method(func: Callable[..., Any]) -> bool:
"""
检查函数是否已实现
模块链每次分发都会调用,结果按代码对象缓存;插件热重载生成新代码对象后自然重新判定,
旧代码对象回收时缓存项随之释放。
"""
target = inspect.unwrap(func)
code = getattr(getattr(target, "__func__", target), "__code__", None)
if code is None:
return ObjectUtils._check_method_uncached(func, None)
cached = _implemented_cache.get(code)
if cached is None:
cached = ObjectUtils._check_method_uncached(func, code)
with _implemented_cache_lock:
_implemented_cache[code] = cached
return cached
@staticmethod
def _check_method_uncached(func: Callable[..., Any], code: Optional[CodeType]) -> bool:
"""按源码语法判断函数体是否只有占位语句,源码不可得时退回字节码判断。"""
try:
src = inspect.getsource(func)
tree = ast.parse(textwrap.dedent(src))
if code is None:
raise OSError("callable without code object")
tree = ast.parse(textwrap.dedent(_read_function_source(code)))
node = tree.body[0]
if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
return True
body = node.body
for stmt in body:
# 跳过 pass
if isinstance(stmt, ast.Pass):
continue
# 跳过 docstring 或 ...
if isinstance(stmt, ast.Expr):
expr = stmt.value
if isinstance(expr, ast.Constant):
if isinstance(expr.value, str) or expr.value is Ellipsis:
continue
# 检查 raise NotImplementedError
if isinstance(stmt, ast.Raise):
exc = stmt.exc
if isinstance(exc, ast.Call) and getattr(exc.func, "id", None) == "NotImplementedError":
continue
if isinstance(exc, ast.Name) and exc.id == "NotImplementedError":
continue
return True
return False
return not all(_is_placeholder_stmt(stmt) for stmt in node.body)
except Exception:
# 源代码分析失败时,进行字节码分析
code_obj = func.__code__ # type: ignore[attr-defined]

View File

@@ -1,5 +1,6 @@
user moviepilot;
worker_processes auto;
# 只代理单用户 WebUI 与 API,固定 2 个 worker 足够,auto 会按 CPU 核数起进程,多核 NAS 上白占内存
worker_processes 2;
pid /var/run/nginx.pid;
worker_cpu_affinity auto;

View File

@@ -1,41 +1,101 @@
from unittest import TestCase
import functools
import linecache
from app.foundation import reflection
from app.foundation.reflection import ObjectUtils
class ObjectUtilsTest(TestCase):
def test_check_method():
def implemented_function():
return "Hello"
def test_check_method(self):
def implemented_function():
return "Hello"
def pass_function():
pass
def docstring_function():
"""This is a docstring."""
def ellipsis_function():
...
def not_implemented_function():
raise NotImplementedError
def not_implemented_function_with_call():
raise NotImplementedError()
async def multiple_lines_async_def(_param1: str,
_param2: str):
pass
def empty_function():
return
assert ObjectUtils.check_method(implemented_function)
assert not ObjectUtils.check_method(pass_function)
assert not ObjectUtils.check_method(docstring_function)
assert not ObjectUtils.check_method(ellipsis_function)
assert not ObjectUtils.check_method(not_implemented_function)
assert not ObjectUtils.check_method(not_implemented_function_with_call)
assert not ObjectUtils.check_method(multiple_lines_async_def)
assert ObjectUtils.check_method(empty_function)
class _Module:
"""模拟模块类:占位方法与已实现方法并存,并经装饰器包装。"""
def placeholder(self):
"""未实现"""
@staticmethod
def _wrap(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@_wrap
def decorated_placeholder(self):
pass
def implemented(self):
return 1
def test_check_method_handles_bound_and_decorated_methods():
module = _Module()
assert not ObjectUtils.check_method(module.placeholder)
assert not ObjectUtils.check_method(module.decorated_placeholder)
assert ObjectUtils.check_method(module.implemented)
def test_check_method_does_not_leave_source_in_linecache():
linecache.clearcache()
def pass_function():
pass
assert not ObjectUtils.check_method(pass_function)
assert __file__ not in linecache.cache
def test_check_method_caches_result_per_code_object(monkeypatch):
calls = []
original = reflection._read_function_source
monkeypatch.setattr(
reflection, "_read_function_source", lambda code: calls.append(code) or original(code)
)
def make():
def pass_function():
pass
def docstring_function():
"""This is a docstring."""
return pass_function
def ellipsis_function():
...
def not_implemented_function():
raise NotImplementedError
def not_implemented_function_with_call():
raise NotImplementedError()
async def multiple_lines_async_def(_param1: str,
_param2: str):
pass
def empty_function():
return
self.assertTrue(ObjectUtils.check_method(implemented_function))
self.assertFalse(ObjectUtils.check_method(pass_function))
self.assertFalse(ObjectUtils.check_method(docstring_function))
self.assertFalse(ObjectUtils.check_method(ellipsis_function))
self.assertFalse(ObjectUtils.check_method(not_implemented_function))
self.assertFalse(ObjectUtils.check_method(not_implemented_function_with_call))
self.assertFalse(ObjectUtils.check_method(multiple_lines_async_def))
self.assertTrue(ObjectUtils.check_method(empty_function))
# 两个闭包共享同一代码对象,只读取一次源码
first, second = make(), make()
assert not ObjectUtils.check_method(first)
assert not ObjectUtils.check_method(second)
assert calls == [first.__code__]