orchestrator_watch.py 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. from django.core.management.base import BaseCommand
  2. class Command(BaseCommand):
  3. help = "Watch the runserver autoreload PID file and restart orchestrator on reloads."
  4. def add_arguments(self, parser):
  5. parser.add_argument(
  6. "--pidfile",
  7. default=None,
  8. help="Path to runserver pidfile to watch",
  9. )
  10. parser.add_argument(
  11. "--interval",
  12. type=float,
  13. default=1.0,
  14. help="Polling interval in seconds",
  15. )
  16. def handle(self, *args, **kwargs):
  17. import os
  18. import time
  19. from archivebox.config.common import STORAGE_CONFIG
  20. from archivebox.machine.models import Process, Machine
  21. from archivebox.workers.orchestrator import Orchestrator
  22. os.environ['ARCHIVEBOX_ORCHESTRATOR_WATCHER'] = '1'
  23. pidfile = kwargs.get("pidfile") or os.environ.get("ARCHIVEBOX_RUNSERVER_PIDFILE")
  24. if not pidfile:
  25. pidfile = str(STORAGE_CONFIG.TMP_DIR / "runserver.pid")
  26. interval = max(0.2, float(kwargs.get("interval", 1.0)))
  27. last_pid = None
  28. def restart_orchestrator():
  29. Process.cleanup_stale_running()
  30. machine = Machine.current()
  31. running = Process.objects.filter(
  32. machine=machine,
  33. status=Process.StatusChoices.RUNNING,
  34. process_type__in=[
  35. Process.TypeChoices.ORCHESTRATOR,
  36. Process.TypeChoices.WORKER,
  37. Process.TypeChoices.HOOK,
  38. ],
  39. )
  40. for proc in running:
  41. try:
  42. if proc.process_type == Process.TypeChoices.HOOK:
  43. proc.kill_tree(graceful_timeout=0.5)
  44. else:
  45. proc.terminate(graceful_timeout=1.0)
  46. except Exception:
  47. continue
  48. if not Orchestrator.is_running():
  49. Orchestrator(exit_on_idle=False).start()
  50. while True:
  51. try:
  52. if os.path.exists(pidfile):
  53. with open(pidfile, "r") as handle:
  54. pid = handle.read().strip() or None
  55. else:
  56. pid = None
  57. if pid and pid != last_pid:
  58. restart_orchestrator()
  59. last_pid = pid
  60. elif not Orchestrator.is_running():
  61. Orchestrator(exit_on_idle=False).start()
  62. except Exception:
  63. pass
  64. time.sleep(interval)