demo_task_log_store.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. """End-to-end demo: TaskLogStore against real MySQL.
  2. Simulates the 13-task driver writing per-task results to the task_logs
  3. table, then demonstrates the resume / query patterns:
  4. 1. setup: create user + server + zone + run via the other Stores
  5. 2. append 5 task logs (t01..t05) with mixed success/failure
  6. 3. list_for_run: read all logs back in order
  7. 4. list_for_run_task: get the latest log for a specific task
  8. 5. count_for_run: total / success / failure counts
  9. 6. completed_task_ids: the set the resume logic would use
  10. Run:
  11. uv run python scripts/demo_task_log_store.py
  12. uv run python scripts/demo_task_log_store.py --cleanup
  13. """
  14. from __future__ import annotations
  15. import argparse
  16. import sys
  17. from datetime import datetime
  18. from pathlib import Path
  19. _HERE = Path(__file__).resolve().parent
  20. sys.path.insert(0, str(_HERE.parent / "src"))
  21. from cq2_sdk.env_loader import load_env # noqa: E402
  22. _PROJECT_ROOT = _HERE.parent
  23. load_env(_PROJECT_ROOT / ".env")
  24. from cq2_sdk.db_models import Zone as DbZone # noqa: E402
  25. from cq2_sdk.store import ( # noqa: E402
  26. RunStore,
  27. ServerStore,
  28. TaskLogStore,
  29. UserStore,
  30. ZoneStore,
  31. connection,
  32. )
  33. TEST_USERNAME = "demo_task_log_user"
  34. TEST_SERVER = "DEMO_TASKLOG_SERVER_玖扬复古176"
  35. TEST_ZONE_ID = "demo_tasklog_zone_001"
  36. def setup_run(servers, zones, users, runs) -> tuple[int, int, int]:
  37. """Create user + server + zone + run. Returns (user_id, server_id, run_id)."""
  38. srv = servers.upsert(TEST_SERVER, 99999999, game_id=206923)
  39. zones.bulk_upsert(srv.id, [
  40. DbZone(zone_id=TEST_ZONE_ID, zone_name="TaskLog Demo",
  41. host="demo.host", port=12345,
  42. open_time=datetime.now().replace(microsecond=0),
  43. status="open"),
  44. ])
  45. user = users.upsert_by_username(TEST_USERNAME, "demo_token_for_tasklog")
  46. run = runs.create(user_id=user.id, server_id=srv.id, zone_id=1)
  47. return user.id, srv.id, run.id
  48. def cleanup_run(cur, run_id: int, user_id: int) -> None:
  49. cur.execute("DELETE FROM task_logs WHERE run_id = %s", (run_id,))
  50. cur.execute("DELETE FROM runs WHERE id = %s", (run_id,))
  51. cur.execute("DELETE FROM users WHERE id = %s", (user_id,))
  52. def main() -> int:
  53. parser = argparse.ArgumentParser(description="Demo TaskLogStore")
  54. parser.add_argument("--cleanup", action="store_true",
  55. help="Delete the test user/run/logs after demo")
  56. args = parser.parse_args()
  57. print("=" * 70)
  58. print("TaskLogStore end-to-end demo")
  59. print("=" * 70)
  60. with connection() as conn:
  61. servers = ServerStore(conn)
  62. zones = ZoneStore(conn)
  63. users = UserStore(conn)
  64. runs = RunStore(conn)
  65. logs = TaskLogStore(conn)
  66. # ---------- setup ----------
  67. print("\n[setup] create user + server + zone + run...")
  68. user_id, server_id, run_id = setup_run(servers, zones, users, runs)
  69. print(f" user_id={user_id} server_id={server_id} run_id={run_id}")
  70. # ---------- 1. append 5 task logs ----------
  71. print("\n[1] TaskLogStore.append() — simulate 5 task results")
  72. scenarios = [
  73. ("t01", True, 2.1, None),
  74. ("t02", True, 5.4, None),
  75. ("t03", False, 1.0, "geetest validate token expired"),
  76. ("t04", True, 0.8, None),
  77. ("t05", True, 3.2, None),
  78. ]
  79. appended_ids: list[int] = []
  80. for tid, ok, elapsed, err in scenarios:
  81. log = logs.append(run_id=run_id, task_id=tid, success=ok,
  82. elapsed_seconds=elapsed, error=err)
  83. appended_ids.append(log.id)
  84. status = "OK " if ok else "ERR"
  85. err_part = f" err={err!r}" if err else ""
  86. print(f" append({tid}, {status}) -> id={log.id} "
  87. f"elapsed={elapsed}s{err_part}")
  88. # ---------- 2. list_for_run ----------
  89. print("\n[2] TaskLogStore.list_for_run(run_id) — read back in order")
  90. all_logs = logs.list_for_run(run_id)
  91. for l in all_logs:
  92. mark = "[OK]" if l.success else "[ERR]"
  93. err_part = f" err={l.error!r}" if l.error else ""
  94. print(f" {mark} {l.task_id} started={l.started_at} "
  95. f"elapsed={l.elapsed_seconds}s{err_part}")
  96. # ---------- 3. list_for_run_task ----------
  97. print("\n[3] TaskLogStore.list_for_run_task(run_id, 't03') — "
  98. "find the failure")
  99. fail_log = logs.list_for_run_task(run_id, "t03")
  100. assert fail_log is not None
  101. assert fail_log.success is False
  102. assert fail_log.error == "geetest validate token expired"
  103. print(f" t03.success = {fail_log.success} error = {fail_log.error!r}")
  104. # ---------- 4. counts ----------
  105. print("\n[4] TaskLogStore.count_for_run(run_id) — summary")
  106. total = logs.count_for_run(run_id)
  107. ok = logs.count_for_run(run_id, success=True)
  108. fail = logs.count_for_run(run_id, success=False)
  109. print(f" total = {total}")
  110. print(f" success= {ok}")
  111. print(f" fail = {fail}")
  112. assert total == 5 and ok == 4 and fail == 1
  113. # ---------- 5. completed_task_ids ----------
  114. print("\n[5] TaskLogStore.completed_task_ids(run_id) — resume set")
  115. done = logs.completed_task_ids(run_id)
  116. print(f" completed = {sorted(done)}")
  117. # E2 resume logic would skip these and only run t06..t13
  118. assert done == {"t01", "t02", "t04", "t05"}
  119. # ---------- summary ----------
  120. print("\n[summary] persisted in MySQL 996cq:")
  121. print(f" task_logs: 5 rows for run_id={run_id} "
  122. f"(4 success, 1 failure)")
  123. print(f" ids: {appended_ids}")
  124. if args.cleanup:
  125. print("\n[cleanup] deleting test user/run/logs...")
  126. with conn.cursor() as cur:
  127. cleanup_run(cur, run_id, user_id)
  128. print(" done")
  129. else:
  130. print("\n (use --cleanup to delete the test rows)")
  131. print("\n[OK] demo complete.")
  132. return 0
  133. if __name__ == "__main__":
  134. sys.exit(main())