142 lines
4.0 KiB
Python
142 lines
4.0 KiB
Python
"""Проверка пунктов прожарки на текущем коде (до патчей). Только чтение, ничего не пишет в базу."""
|
|
import gc
|
|
import json
|
|
import os
|
|
import re
|
|
import sys
|
|
import threading
|
|
import time
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
os.environ.setdefault("PYTHONIOENCODING", "utf-8")
|
|
|
|
import psutil # noqa: E402
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
OUT = {}
|
|
|
|
|
|
def p(k, v):
|
|
OUT[k] = v
|
|
print(f"{k}: {v}")
|
|
|
|
|
|
# 2. валидация limit: эмулируем то, что делает serve для /api/items
|
|
def check_limit():
|
|
def parse(raw):
|
|
try:
|
|
return min(int(raw), 200)
|
|
except ValueError as e:
|
|
return f"ValueError: {e}"
|
|
|
|
p("limit[-1]", parse("-1"))
|
|
p("limit[abc]", parse("abc"))
|
|
p("limit[999999]", parse("999999"))
|
|
|
|
|
|
# 3. утечка соединений
|
|
def check_conn_leak():
|
|
from omni import store
|
|
gc.collect()
|
|
before = len(psutil.Process().open_files())
|
|
for _ in range(50):
|
|
store.top(limit=1)
|
|
gc.collect()
|
|
after = len(psutil.Process().open_files())
|
|
p("fd_before", before)
|
|
p("fd_after_50_calls", after)
|
|
p("fd_leak", after - before)
|
|
|
|
|
|
# 4. троттлинг
|
|
def check_throttle():
|
|
from omni import net
|
|
net._cfg["per_host_delay"] = 0.3
|
|
net._last.clear()
|
|
t0 = time.time()
|
|
threads = [threading.Thread(target=lambda: [net._throttle("example.org") for _ in range(3)]) for _ in range(8)]
|
|
for t in threads:
|
|
t.start()
|
|
for t in threads:
|
|
t.join()
|
|
el = time.time() - t0
|
|
expected = 0.3 * (8 * 3 - 1)
|
|
p("throttle_elapsed", round(el, 2))
|
|
p("throttle_expected_min", round(expected, 2))
|
|
p("throttle_broken", el < expected * 0.5)
|
|
|
|
|
|
# 6. мёртвые файлы
|
|
def check_dead():
|
|
names = ["abuse_sources", "rank", "sources_abuse"]
|
|
for n in names:
|
|
hits = []
|
|
for f in (ROOT / "omni").glob("*.py"):
|
|
if f.stem == n:
|
|
continue
|
|
txt = f.read_text(encoding="utf-8", errors="replace")
|
|
if re.search(rf"(import|from)\s+.*\b{n}\b", txt):
|
|
hits.append(f.name)
|
|
p(f"imports_{n}", hits)
|
|
|
|
|
|
# 7. дубли источников
|
|
def check_dupe_sources():
|
|
from omni import sources
|
|
urls = [s.url for s in sources.all_sources()]
|
|
p("sources_total", len(urls))
|
|
p("urls_unique", len(set(urls)))
|
|
p("dupe_groups", len(urls) - len(set(urls)))
|
|
|
|
|
|
# 8. except Exception
|
|
def check_except():
|
|
total = 0
|
|
for f in (ROOT / "omni").rglob("*.py"):
|
|
total += len(re.findall(r"except\s+Exception", f.read_text(encoding="utf-8", errors="replace")))
|
|
p("except_exception_total", total)
|
|
|
|
|
|
# 9. JOBS без eviction
|
|
def check_jobs():
|
|
from omni import serve
|
|
src = (ROOT / "omni" / "serve.py").read_text(encoding="utf-8")
|
|
p("jobs_has_eviction", "JOBS.pop" in src or "del JOBS" in src)
|
|
for i in range(500):
|
|
serve._job(f"j{i}", status="done")
|
|
p("jobs_len_after_500", len(serve.JOBS))
|
|
|
|
|
|
# 10. sleep в ai.py
|
|
def check_ai_sleep():
|
|
src = (ROOT / "omni" / "ai.py").read_text(encoding="utf-8")
|
|
m = re.search(r"except Exception:\s*\n\s*time\.sleep\(([^)]*)\)", src)
|
|
p("ai_sleep_line", m.group(0).replace("\n", " ") if m else None)
|
|
|
|
|
|
# 1. /run в боте
|
|
def check_bot_run():
|
|
src = (ROOT / "omni" / "bot.py").read_text(encoding="utf-8")
|
|
p("bot_new_plus_equals_add", "new += store.add(items)" in src)
|
|
p("bot_cmd_abuse_present", "def cmd_abuse" in src)
|
|
p("bot_cmds_abuse_present", "cmd_abuse" in src)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import contextlib
|
|
import io
|
|
|
|
check_limit()
|
|
with contextlib.redirect_stdout(io.StringIO()):
|
|
check_bot_run()
|
|
check_bot_run()
|
|
check_throttle()
|
|
check_dead()
|
|
check_dupe_sources()
|
|
check_except()
|
|
with contextlib.redirect_stderr(io.StringIO()):
|
|
check_jobs()
|
|
check_ai_sleep()
|
|
check_conn_leak()
|
|
print("\nJSON:", json.dumps(OUT, ensure_ascii=False, default=str)) |