dag.py 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. from __future__ import annotations
  2. from dataclasses import dataclass
  3. @dataclass(frozen=True)
  4. class StepDefinition:
  5. key: str
  6. order: int
  7. dependencies: tuple[str, ...]
  8. timeout_seconds: int
  9. max_attempts: int = 1
  10. retryable: bool = False
  11. critical: bool = True
  12. _RAW_STEPS: tuple[tuple[str, int, bool], ...] = (
  13. ("global_tree_sync", 3600, True),
  14. ("demand_pool_source_sync", 3600, True),
  15. ("demand_classify", 21600, False),
  16. ("demand_belong_rel_sync", 3600, True),
  17. ("real_metrics_sync", 3600, True),
  18. ("popularity_stats", 3600, True),
  19. ("category_tree_weight", 3600, True),
  20. ("source_video_sync", 7200, True),
  21. ("demand_grade", 21600, False),
  22. ("demand_expand", 21600, False),
  23. ("video_discovery", 21600, False),
  24. )
  25. def _build_steps() -> tuple[StepDefinition, ...]:
  26. result: list[StepDefinition] = []
  27. previous: str | None = None
  28. for order, (key, timeout_seconds, retryable) in enumerate(_RAW_STEPS, start=1):
  29. result.append(
  30. StepDefinition(
  31. key=key,
  32. order=order,
  33. dependencies=(previous,) if previous else (),
  34. timeout_seconds=timeout_seconds,
  35. max_attempts=2 if retryable else 1,
  36. retryable=retryable,
  37. )
  38. )
  39. previous = key
  40. return tuple(result)
  41. PIPELINE_STEPS = _build_steps()
  42. STEP_BY_KEY = {step.key: step for step in PIPELINE_STEPS}
  43. def validate_dag() -> None:
  44. seen: set[str] = set()
  45. for step in PIPELINE_STEPS:
  46. if step.key in seen:
  47. raise ValueError(f"Duplicate step key: {step.key}")
  48. missing = set(step.dependencies) - seen
  49. if missing:
  50. raise ValueError(f"Step {step.key} has unresolved dependencies: {sorted(missing)}")
  51. seen.add(step.key)
  52. validate_dag()