scheduler.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. """Compatibility facade over the durable pipeline control plane."""
  2. from __future__ import annotations
  3. from typing import Any
  4. from api.services.pipeline import get_run, pipeline_health
  5. from supply_infra.config import get_infra_settings
  6. from supply_infra.pipeline.dag import PIPELINE_STEPS
  7. from supply_infra.pipeline.dates import CHINA_TIMEZONE, next_schedule_china
  8. from supply_infra.pipeline.run_service import submit_pipeline_run
  9. from supply_infra.scheduler.constants import (
  10. AIGC_PUBLISH_JOB_ID,
  11. AIGC_PUBLISH_JOB_NAME,
  12. GLOBAL_V2_SNAPSHOT_JOB_ID,
  13. GLOBAL_V2_SNAPSHOT_JOB_NAME,
  14. SUPPLY_PIPELINE_JOB_ID,
  15. SUPPLY_PIPELINE_JOB_NAME,
  16. )
  17. from supply_infra.scheduler.jobs.publish_videos_from_discovery import (
  18. publish_videos_from_discovery,
  19. )
  20. def list_triggerable_jobs() -> list[dict[str, Any]]:
  21. settings = get_infra_settings()
  22. return [
  23. {
  24. "id": SUPPLY_PIPELINE_JOB_ID,
  25. "name": SUPPLY_PIPELINE_JOB_NAME,
  26. "description": "提交严格门禁的 11 步持久化供给流水线(不含 AIGC 发布)",
  27. "accepts_biz_dt": True,
  28. "deprecated": False,
  29. "steps": [step.key for step in PIPELINE_STEPS],
  30. },
  31. {
  32. "id": GLOBAL_V2_SNAPSHOT_JOB_ID,
  33. "name": GLOBAL_V2_SNAPSHOT_JOB_NAME,
  34. "description": "独立读取昨天全量 ODPS 数据,按唯一 key 增量写入四张业务表",
  35. "accepts_biz_dt": True,
  36. "deprecated": False,
  37. "steps": [],
  38. },
  39. {
  40. "id": AIGC_PUBLISH_JOB_ID,
  41. "name": AIGC_PUBLISH_JOB_NAME,
  42. "description": (
  43. "扫描未发布的 primary 候选并分发到 AIGC;"
  44. f"调度默认每 {settings.scheduler_aigc_publish_interval_minutes} 分钟执行"
  45. ),
  46. "accepts_biz_dt": True,
  47. "deprecated": False,
  48. "steps": [],
  49. },
  50. ]
  51. def run_scheduler_job(
  52. job_id: str,
  53. *,
  54. biz_dt: str | None = None,
  55. ) -> dict[str, Any]:
  56. if job_id == SUPPLY_PIPELINE_JOB_ID:
  57. return submit_pipeline_run(
  58. biz_dt=biz_dt,
  59. trigger_type="api",
  60. trigger_source="legacy_scheduler_api",
  61. ).to_dict()
  62. if job_id == AIGC_PUBLISH_JOB_ID:
  63. return publish_videos_from_discovery(
  64. biz_dt=biz_dt,
  65. skip_published=True,
  66. any_biz_dt=biz_dt is None,
  67. )
  68. if job_id == GLOBAL_V2_SNAPSHOT_JOB_ID:
  69. from supply_infra.scheduler.jobs.sync_global_v2_snapshot import (
  70. sync_global_v2_snapshot,
  71. )
  72. return sync_global_v2_snapshot(partition_date=biz_dt)
  73. raise KeyError(job_id)
  74. def get_scheduler_job_run(run_id: str) -> dict[str, Any] | None:
  75. return get_run(run_id)
  76. def run_supply_pipeline(*, biz_dt: str | None = None) -> dict[str, Any]:
  77. return run_scheduler_job(SUPPLY_PIPELINE_JOB_ID, biz_dt=biz_dt)
  78. def scheduler_status() -> dict[str, Any]:
  79. settings = get_infra_settings()
  80. health = pipeline_health()
  81. return {
  82. "enabled": settings.scheduler_enabled,
  83. "running": health["scheduler_running"],
  84. "embedded": False,
  85. "deprecated": True,
  86. "timezone": settings.scheduler_timezone,
  87. "jobs": [
  88. {
  89. "id": SUPPLY_PIPELINE_JOB_ID,
  90. "name": SUPPLY_PIPELINE_JOB_NAME,
  91. "next_run_time": next_schedule_china(settings=settings)
  92. .replace(tzinfo=CHINA_TIMEZONE)
  93. .isoformat(),
  94. },
  95. {
  96. "id": GLOBAL_V2_SNAPSHOT_JOB_ID,
  97. "name": GLOBAL_V2_SNAPSHOT_JOB_NAME,
  98. "cron": "0 6 * * *",
  99. "timezone": "Asia/Shanghai",
  100. },
  101. {
  102. "id": AIGC_PUBLISH_JOB_ID,
  103. "name": AIGC_PUBLISH_JOB_NAME,
  104. "interval_minutes": settings.scheduler_aigc_publish_interval_minutes,
  105. },
  106. ],
  107. **health,
  108. }