"""HTTP-стресс против contracts-flask (standalone, не pytest). Создаёт параллельную нагрузку на /upload большими файлами + /health, замеряет RPS, ошибки, таймауты. Цель — «разогреть» сервис и выявить деградацию/обрывы (шлюз, OOM, SQLite). Запуск: python tests/load/stress_http.py --url http://127.0.0.1:5000 \ --n 1000 --threads 32 --size 100000 Пример против прода (осторожно): python tests/load/stress_http.py --url https://contractor.pythonk8s.dev.nubes.ru \ --n 200 --threads 16 --size 500000 """ import argparse import time from concurrent.futures import ThreadPoolExecutor import httpx def main(): ap = argparse.ArgumentParser(description="HTTP-стресс contracts-flask") ap.add_argument("--url", default="http://127.0.0.1:5000") ap.add_argument("--n", type=int, default=1000, help="число запросов") ap.add_argument("--threads", type=int, default=32, help="параллельных потоков") ap.add_argument("--size", type=int, default=100000, help="размер файла в байтах (если не задан --file)") ap.add_argument("--file", default=None, help="путь к реальному файлу (заменяет сгенерированный)") ap.add_argument("--health-only", action="store_true", help="только /health, без загрузки файлов") args = ap.parse_args() if args.file: import os as _os fname = _os.path.basename(args.file) with open(args.file, "rb") as _f: payload = _f.read() real_size = len(payload) else: fname = "load.docx" payload = b"x" * args.size real_size = args.size ok = 0 err = 0 slow = 0 t0 = time.time() def one(_): nonlocal ok, err, slow try: with httpx.Client(timeout=30) as c: if args.health_only: r = c.get(f"{args.url}/health") else: r = c.post( f"{args.url}/upload", files={"files": (fname, payload, "application/octet-stream")}, ) if r.status_code == 200: ok += 1 else: err += 1 except httpx.TimeoutException: err += 1 slow += 1 except Exception: err += 1 with ThreadPoolExecutor(max_workers=args.threads) as ex: list(ex.map(one, range(args.n))) elapsed = time.time() - t0 print(f"url={args.url}") print(f"n={args.n} threads={args.threads} size={real_size}B file={args.file or '(gen)'} health_only={args.health_only}") print(f"ok={ok} err={err} timeouts={slow} elapsed={elapsed:.1f}s rps={args.n / elapsed:.1f}") if __name__ == "__main__": main()