|
| 1 | +# Cross-Platform Scheduler Options |
| 2 | + |
| 3 | +## Question |
| 4 | + |
| 5 | +For PostgreSQL / pgBackRest backup jobs, what are the viable cross-platform scheduling approaches? |
| 6 | + |
| 7 | +## Sources |
| 8 | + |
| 9 | +- systemd timers: https://www.freedesktop.org/software/systemd/man/systemd.timer.html |
| 10 | +- Windows scheduled tasks: https://learn.microsoft.com/en-us/powershell/module/scheduledtasks/register-scheduledtask |
| 11 | +- macOS launchd timed jobs: https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/ScheduledJobs.html |
| 12 | +- PM2 restart strategies: https://pm2.keymetrics.io/docs/usage/restart-strategies/ |
| 13 | +- PM2 startup persistence: https://pm2.keymetrics.io/docs/usage/startup/ |
| 14 | +- Windows app notifications: https://learn.microsoft.com/en-us/windows/apps/develop/notifications/app-notifications/app-notifications-quickstart |
| 15 | +- Go scheduler library: https://pkg.go.dev/github.com/go-co-op/gocron/v2 |
| 16 | +- Go Windows toast library: https://pkg.go.dev/github.com/go-toast/go-toast |
| 17 | +- Go cross-platform notification library: https://github.com/gen2brain/beeep |
| 18 | +- Local systemd-service-manager docs: `scripts/bash/systemd-service-manager/README.md` |
| 19 | +- Local PM2 pattern: `config/network/rathole/start.ps1` |
| 20 | + |
| 21 | +## Options |
| 22 | + |
| 23 | +### Option A: Native OS Scheduler Adapters |
| 24 | + |
| 25 | +Generate or document scheduler targets per OS: |
| 26 | + |
| 27 | +- Linux: systemd timer, preferably through this repo's `systemd-service-manager` when available. |
| 28 | +- Windows: Task Scheduler via `Register-ScheduledTask`. |
| 29 | +- macOS: launchd plist with `StartCalendarInterval`. |
| 30 | + |
| 31 | +Pros: Best reliability, native logs/lifecycle, reboot behavior, permissions, and service account support. |
| 32 | +Cons: More templates and validation paths; no single file works unchanged on all OSes. |
| 33 | + |
| 34 | +### Option B: PM2 as a Cross-Platform Scheduler Layer |
| 35 | + |
| 36 | +Use PM2 ecosystem files with `cron_restart` to trigger the backup app at scheduled times. |
| 37 | + |
| 38 | +Pros: Similar commands across Windows/macOS/Linux when Node + PM2 are installed; repo already has PM2 patterns. |
| 39 | +Cons: PM2's cron model is restart-oriented, so one-shot backups need careful `autorestart` / exit-code settings. |
| 40 | + |
| 41 | +### Option C: Plain cron |
| 42 | + |
| 43 | +Use crontab on Unix-like systems. |
| 44 | + |
| 45 | +Pros: Simple, common, very low ceremony. |
| 46 | +Cons: Not Windows-native; weaker logs/status; less explicit environment and missed-run behavior than systemd timers. |
| 47 | + |
| 48 | +### Option D: Container / Platform Scheduler |
| 49 | + |
| 50 | +Use Kubernetes CronJob, CI schedule with self-hosted runner, NAS scheduler, or another orchestration platform. |
| 51 | + |
| 52 | +Pros: Good when the deployment platform already exists. |
| 53 | +Cons: Overkill for a local macmini/Tailscale backup setup; ties backups to a specific platform. |
| 54 | + |
| 55 | +### Option E: Long-Running PowerShell Scheduler Loop |
| 56 | + |
| 57 | +Run a persistent `pwsh` process that sleeps and invokes the toolkit on schedule. |
| 58 | + |
| 59 | +Pros: The script itself can be cross-platform. |
| 60 | +Cons: Recreates a supervisor poorly; still needs PM2/systemd/Task Scheduler/launchd to keep it alive. Not recommended for backup reliability. |
| 61 | + |
| 62 | +### Option F: Node Scheduler Service Managed by PM2 |
| 63 | + |
| 64 | +Run a small long-lived Node service under PM2. The Node service owns schedule parsing, non-overlap, logging, and child-process execution of `Postgres-Toolkit.ps1 pgbackrest ...`; PM2 owns process supervision and reboot persistence. |
| 65 | + |
| 66 | +Potential implementation shape: |
| 67 | + |
| 68 | +- Source: `scripts/node/src/postgres-scheduler/**` or a dedicated `scripts/node/src/pgbackrest-scheduler/**`. |
| 69 | +- Config: committed example under `config/database/backup/pgBackRest/`, real `.local` ignored. |
| 70 | +- PM2 app: one fork-mode instance, no cluster mode. |
| 71 | +- Scheduler library: `node-cron` is enough for MVP because it supports timezone and no-overlap scheduling options. |
| 72 | +- Execution: use `node:child_process` to spawn `pwsh -NoProfile -File <Postgres-Toolkit.ps1> pgbackrest ...`. |
| 73 | + |
| 74 | +Pros: One operational model across Windows/macOS/Linux when Node + PM2 are available; better logs and retry behavior than PM2 `cron_restart` alone; avoids OS-specific unit/plist/task templates for the MVP. |
| 75 | + |
| 76 | +Cons: It is a real service that must be tested and maintained; missed-run semantics after downtime must be defined; single-instance enforcement matters if PM2 cluster mode or multiple machines run the same schedule. |
| 77 | + |
| 78 | +Windows notification note: Node can use a notification library or call a PowerShell notification helper after each backup, but Windows toast reliability still depends on running in the interactive user's session with a stable app identity. If PM2 is installed as a background service under `SYSTEM`, desktop notifications may not appear for the logged-in user. |
| 79 | + |
| 80 | +### Option G: Go Scheduler Service Managed by PM2 |
| 81 | + |
| 82 | +Build a small Go service binary and let PM2 manage it with `interpreter: none`. The Go service owns schedule parsing, non-overlap, command execution, and notification dispatch; PM2 still owns process supervision and restart. |
| 83 | + |
| 84 | +Potential implementation shape: |
| 85 | + |
| 86 | +- Source: a new Go module under `projects/clis/pgbackrest-scheduler` or a similar project path. |
| 87 | +- Scheduler library: `github.com/go-co-op/gocron/v2`, which supports scheduler location/timezone, concurrency limiting, monitors, and locking-related extension points. |
| 88 | +- Windows notifications: |
| 89 | + - `github.com/go-toast/go-toast` for Windows-specific toast notifications. It exposes `AppID`, `Title`, `Message`, icon/audio/action fields, then invokes PowerShell to display the toast. |
| 90 | + - `github.com/gen2brain/beeep` for a simpler cross-platform notification abstraction. |
| 91 | +- Execution: use `os/exec` to run `pwsh -NoProfile -File <Postgres-Toolkit.ps1> pgbackrest ...`. |
| 92 | + |
| 93 | +Pros: Best runtime distribution story; one compiled binary, low memory, no Node runtime for the service itself; Go scheduling and process execution are a natural fit. |
| 94 | + |
| 95 | +Cons: This repo currently has no Go module/service precedent, so it introduces a new build/test/release lane. Windows toast constraints remain the same as Node: the process must run where the logged-in user can receive desktop notifications. |
| 96 | + |
| 97 | +## Windows Notification Design Notes |
| 98 | + |
| 99 | +Windows desktop toast is not just a language choice. Key constraints: |
| 100 | + |
| 101 | +- The scheduler must run in the interactive user's context if the goal is a local desktop toast. |
| 102 | +- A stable app identity/AppID is important for notification grouping and reliability. |
| 103 | +- Running as a Windows service or under `SYSTEM` is good for unattended operation but bad for per-user desktop notifications. |
| 104 | +- If the backup actually runs on macmini/Linux and the user wants a Windows desktop toast, the scheduler/notification bridge needs to run on the Windows machine or send a remote notification through another channel such as ntfy, email, webhook, Telegram, or Teams. |
| 105 | + |
| 106 | +This means the project should separate notification delivery from backup execution. A scheduler can emit: |
| 107 | + |
| 108 | +- local desktop notification, |
| 109 | +- log-only notification, |
| 110 | +- webhook notification, |
| 111 | +- email or ntfy notification, |
| 112 | +- disabled notification. |
| 113 | + |
| 114 | +## Recommendation |
| 115 | + |
| 116 | +Use a cross-platform backup CLI plus scheduler adapters: |
| 117 | + |
| 118 | +1. Keep `Postgres-Toolkit.ps1 pgbackrest ...` as the only backup execution path. |
| 119 | +2. Expose the toolkit through `tool.psd1` and `Manage-BinScripts.ps1`. |
| 120 | +3. For MVP, either support PM2 ecosystem templates directly, or build a Node/Go scheduler service managed by PM2 if richer logs/retry/non-overlap/notification behavior is required. |
| 121 | +4. Document native alternatives for Linux, Windows, and macOS; avoid pretending that one scheduler is equally native everywhere. |
| 122 | + |
| 123 | +Future toolkit command shape could be: |
| 124 | + |
| 125 | +```powershell |
| 126 | +Postgres-Toolkit.ps1 schedule render --target pm2 --type incr --schedule '0 3 * * *' |
| 127 | +Postgres-Toolkit.ps1 schedule render --target systemd --type incr --schedule '0 3 * * *' |
| 128 | +Postgres-Toolkit.ps1 schedule render --target windows-task --type incr --schedule daily |
| 129 | +Postgres-Toolkit.ps1 schedule render --target launchd --type incr --schedule daily |
| 130 | +``` |
| 131 | + |
| 132 | +If choosing the Node service route, the MVP command shape can stay simpler: |
| 133 | + |
| 134 | +```bash |
| 135 | +pm2 start config/database/backup/pgBackRest/pgbackrest-scheduler.pm2.config.cjs |
| 136 | +pm2 logs pgbackrest-scheduler |
| 137 | +pm2 save |
| 138 | +``` |
0 commit comments