Hacker News new | ask | show | jobs
by jeroenhd 9 days ago
Having had to work on an application supposedly supporting cron expressions: the numbers are just the basic parts of the language.

When someone inputs something ridiculous like "5,3/4 4-8,11 1 4,5,6,9-11 */2" you get to enjoy the fun of reverse engineering what they meant (it's never what they actually wrote).

And that's before you get to all the extensions supported in some cron environments (but not all).

I find systemd timers a lot more manageable. Things like having control over whether or not long-running jobs are allowed to overlap and the ability to run tasks between start-finish rather than a fixed time window are major improvements for me. At some point my VPS went down because the backup job ran into some kind of symlink loop and cron just kept spawning more and more backup tasks even though none of them finished.

Having to re-write commands and scripts because CRON had its own special PATH was also a pain point, but the same can be true for some types of systemd timers. But: you can execute those timers manually if you want instead of updating the crontab to trigger in 30 seconds and simply waiting.

3 comments

Per other comments though, it looks like systemd's syntax when you want to specify something that's not just that one number is at least as complex.

Is your example (which I agree, looks cryptic) any less cryptic in systemd?

I asked jippity, and it said this:

    [Timer]
    OnCalendar=*-04,05,06,09,10,11-01 04..08,11:03/4,05:00
    OnCalendar=Sun,Tue,Thu,Sat *-04,05,06,09,10,11-* 04..08,11:03/4,05:00
To which I have to go: "what?"

> Things like having control over whether or not long-running jobs are allowed to overlap

With cron that's just prefixing the command with `flock -n <lock>`, but sure the "pick somewhere to put the lock" is probably better with systemd.

> Having to re-write commands and scripts because CRON had its own special PATH

Why? Wouldn't you just put that in the crontab? I don't even see this as different. It's in the cron config or the systemd timer config.

The other improvements you mentioned seem good.

This is like a complaint about regex syntax. It's impossible to comprehend a non-trivial regex in a second or two. However, if you know the rules, it's trivial to step through it. What's the point of complaining? There's no representation that anyone could grok on first impression. This is much simpler than regex.

Nobody's prevented from using cron instead of systemd timers. The significant differences in typical relatively simple cases are ordering:

cron: M H d m Y DOW

systemd: DOW Y-m-d H:M:S [each part optional, with *, *, and 00:00:00 defaults]

And then, because - is taken, ranges use .. in systemd. Aside from that, it's mostly the same for typical cases of simple periodic timers. Even x/n and x-y/n for steps work similarly. Syntax for complex cases start to diverge, for jitter or special numerically-irregular DOW or DOM or multiple non-periodic times.

In your example, adding more spaces between the date and time parts would make it more visually digestible. There's also the .. range operator which jippity strangely didn't use for the month field even though it did for the hours field.

Yup, entirely agree. That's why I thought it odd of jeroenhd to bring it up as if it's an argument against cron or cron time specs.

From what I see not better or worse, it's just different.

> 5,3/4 4-8,11 1 4,5,6,9-11 */2

What's so hard about "At 5 minutes past the hour and every 4 minutes, starting at 3 minutes past the hour, at 04:00 AM through 08:59 AM and 11:00 AM, on day 1 of the month, every 2 days of the week, only in April, May, June, and September through November"?

(I used https://crontab.cronhub.io/ to decode it, to be fair)

Complex expressions are one of the things I don't like in cron. On Debian/Ubuntu servers, I just bite the bullet with systemd timers. On my workstation, I have a personal job scheduler that feels easier and more fun to tinker with. The scheduler uses Starlark functions instead. For example:

  # Run if at least a day has passed since the last run
  # and it isn't the weekend.
  def should_run(finished, timestamp, dow, **_):
      return dow not in [0, 6] and timestamp - finished >= one_day
This was inspired by GNU mcron. In mcron, jobs can calculate the next time they should run using Guile (https://www.gnu.org/software/mcron/manual/mcron.html#Guile-S...):

  (job
     '(next-minute-from
        (next-hour (range 0 24 2))
        '(15))
     "my-program")
I found mcron's scheduling counterintuitive and decided I wanted a function that returned a boolean. I can tentatively recommend it.