Spaces:
Sleeping
Sleeping
| """Worker engine recommend — mode queue (bàn giao 14, 04/08/2026). | |
| Process TÁCH KHỎI API: nạp env + JIT Numba MỘT lần lúc boot, xong hết mới bật | |
| heartbeat + nhận job (bối cảnh #3: JIT nguội ~40s/process, health không được | |
| báo alive lúc còn đang JIT). Vòng đời sau đó: | |
| BRPOP pc:jobs:recommend → app.engine.handle_job (MỘT code path với | |
| in-process — bẫy 27/07) → LPUSH pc:result:{job_id} (TTL dọn rác) | |
| Heartbeat: SET pc:worker:heartbeat TTL 15s mỗi vòng chờ — BRPOP timeout 5s | |
| nên nhịp ~5s lúc rảnh, mỗi job lúc bận (search gate < 5s/cú → không hụt TTL). | |
| Ctrl+C thoát sạch: xoá heartbeat để /api/health thấy worker chết NGAY thay vì | |
| đợi TTL; job đang dở vẫn trả reply xong mới thoát (BRPOP là điểm ngắt). | |
| Chạy tay (Redis: docker compose -f docker-compose.dev.yml up -d redis): | |
| set REDIS_URL=redis://localhost:6379/0 | |
| python scripts\\engine_worker.py | |
| Launcher: D:\\Khoa luan\\run_engine_worker.bat (ngoài repo, như mọi launcher). | |
| """ | |
| from __future__ import annotations | |
| import os | |
| import sys | |
| import time | |
| from pathlib import Path | |
| # Repo KHÔNG cài package: src/ cho poolcoach_rl, ROOT cho app.* (quy ước | |
| # scripts/ — xem CLAUDE.md; thiếu dòng này là lỗi đã tái phạm 24/07). | |
| ROOT = Path(__file__).resolve().parents[1] | |
| for _p in (ROOT / "src", ROOT): | |
| if str(_p) not in sys.path: | |
| sys.path.insert(0, str(_p)) | |
| from app import engine, jobqueue # noqa: E402 | |
| def main() -> int: | |
| if not os.environ.get("REDIS_URL"): | |
| print("engine_worker: thieu env REDIS_URL -- worker chi co nghia o " | |
| "mode queue (vi du: redis://localhost:6379/0)", flush=True) | |
| return 2 | |
| jobqueue.setup() | |
| t0 = time.time() | |
| from poolcoach_rl.envs import PositionPlayEnv | |
| from poolcoach_rl.recommend import warmup | |
| env_h = PositionPlayEnv() | |
| print(f"[worker] env san sang sau {time.time() - t0:.1f}s -- warmup JIT " | |
| f"Numba (~40s lan dau)...", flush=True) | |
| warmup_s = warmup(env_h) | |
| print(f"[worker] JIT xong sau {warmup_s:.1f}s -- bat heartbeat, cho job " | |
| f"tren '{jobqueue.JOBS_KEY}'", flush=True) | |
| import redis | |
| served = 0 | |
| try: | |
| while True: | |
| try: | |
| jobqueue.beat() | |
| t_job = time.perf_counter() | |
| if jobqueue.serve_one(lambda p: engine.handle_job(p, env_h), | |
| timeout_s=5): | |
| served += 1 | |
| print(f"[worker] job #{served} xong sau " | |
| f"{time.perf_counter() - t_job:.2f}s", flush=True) | |
| except redis.RedisError as e: | |
| # Redis chết/khởi động lại KHÔNG được giết worker đã JIT ấm | |
| # — chờ rồi thử lại; API phía kia tự trả 503 trong lúc này. | |
| print(f"[worker] mat ket noi Redis ({type(e).__name__}: {e}) " | |
| f"-- thu lai sau 2s", flush=True) | |
| time.sleep(2) | |
| except KeyboardInterrupt: | |
| print(f"\n[worker] Ctrl+C -- thoat sach sau {served} job", flush=True) | |
| finally: | |
| jobqueue.clear_heartbeat() # health thay worker chet NGAY, khong doi TTL | |
| jobqueue.teardown() | |
| return 0 | |
| if __name__ == "__main__": | |
| sys.exit(main()) | |