Skip to content

Scheduling: keep launchd/systemd for cron+browser, crontab is not a fit #11

Description

@huangruoqi

Summary

Investigated switching bin/cron-service and bin/browser-service from launchd/systemd management to plain crontab. Conclusion: keep the current approach — crontab would be a regression, not an improvement.

Current setup

  • bin/cron-service runs cron/server.py, a FastAPI/uvicorn process kept alive by launchd (macOS) / systemd (Linux). Internally it runs routine_scheduler.scheduler_loop, which ticks every 30s and drives per-prospect sweeps on a ~60s cadence, using in-process asyncio.Locks to prevent overlapping runs, plus a live /health endpoint the dashboard polls.
  • bin/browser-service keeps a persistent Chrome process open with a logged-in CDP session (LinkedIn profile), managed the same way.

Why crontab doesn't fit

  1. Browser needs a persistent process, not a periodic job. Crontab only fires commands at intervals; it can't hold a long-lived Chrome/CDP session open. Emulating this would mean a * * * * * "check if it died, restart it" job — reinventing launchd's KeepAlive / systemd's Restart=on-failure, worse.
  2. Sub-minute scheduling granularity. The routine scheduler ticks every 30s and sweeps every ~60s; crontab's floor is 1 minute.
  3. Shared in-process locking. Overlap prevention across sweep kinds currently uses in-memory asyncio.Locks in one long-running process. Under crontab (fresh process per invocation) this would require file-based locking (flock) instead — extra moving parts for no benefit.
  4. Live health endpoint. The dashboard/install.sh rely on an always-up /health HTTP endpoint. Crontab has no persistent process to serve this, so we'd still need a long-running service — putting us back to needing a service manager anyway.
  5. No catch-up after sleep/reboot. macOS has no anacron; missed cron minutes are just skipped. launchd/systemd handle start-on-boot and crash-restart natively. Apple's own docs steer users toward launchd over cron.

Routine state (last_run_at, backoff) is already persisted to disk (dashboard_routines.json), so that part is fine under either model — the blockers are specifically the persistent browser process and sub-minute in-process locking/health-check.

Decision

No code change. Keep launchd (macOS) / systemd (Linux) as the service backend for both cron-service and browser-service.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions