One of the most common feature we expect from our beloved servers is to perform task on a regular, scheduled basis. And since ever the first solution in mind is, of course, cron.

Cron

I’m not going to write yet another article on how cron is configured and how it works. I just will recall some ideas that are needed to follow my path.

Cron is a tool that allows to schedule a job at a specified recurring time. That job will be later performed at that specified time. So, each month, each day at 5.00 PM, but also something more sophisticated like “any last friday of the month”.

Cron offers a minute granularity of scheduling the jobs. Jobs may be added at different levels, in different ways. Any unix user may add its own cron tasks, while the system administrator may also leverage on some predefined folders, like /etc/cron.daily, /etc/cron.hourly, /etc/cron.weekly and so on, that, as the name implies, can be used to drop there scripts that will be later executed with that named frequency.

Most of the applications may be satisfied using those predefined folder, so that, it’s not needed to deal with the cron configuration itself unless new needs. An example configuration file for cron is the following

# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# m h dom mon dow user  command
17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly
25 6    * * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6    * * 7   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6    1 * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
#

The above configuration states that at the minute 17 of every hour will be executed all the script contained in /etc/cron.hourly, at 6.25 AM of each day the content of the /etc/cron.daily, and so on. I pasted it because it will help me to introduce the next step. As I told you I don’t want to go deeper in that syntax.

Well, so… do cron has defects or limits?

Well, actually, there’s one that so far is unsolved. It appears that, even after all the technological miracles we did, a machine that is switched off is still not able to perform any task. And honestly I prefer to keep this power.

So if at the scheduled run of a task the machine is powered off, for any reason, the job execution will be skipped, and will be performed at the next scheduled occourence. If you have a job scheduled to run any night at 3 AM and the system administrator decides that the machine will be shutted down daily between 2 and 4 am, you’ll never see your job executed. Please don’t laugh, I saw this happening in real world!

So, that’s why we throw in the game a new player: anacron.

Anacron

Also anacron is a topic on which it’s easy to find a lot of goog-literature, so I will not spend so much time neither on it.

In anacron we change a bit the principle. We have jobs that we want to be sure are executed at least with the specified frequency. So anacron keeps track of the last execution of a task, and every time it’s started, it checks if the “interval” between two execution is expired. If so, it executes the task (after waiting a specified delay), otherwise it does nothing. Anacron is designed to work at the day granularity, so it’s not supposed to play any role on shortest scheduling.

A typical anacron configuration may sounds as below

# /etc/anacrontab: configuration file for anacron

# See anacron(8) and anacrontab(5) for details.

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
HOME=/root
LOGNAME=root

# These replace cron's entries
1       5       cron.daily      run-parts --report /etc/cron.daily
7       10      cron.weekly     run-parts --report /etc/cron.weekly
@monthly        15      cron.monthly    run-parts --report /etc/cron.monthly

This means: “if not done in the last 1 days, after 5 minutes of delay, execute the scripts in /etc/cron.daily; if not done in the last 7 days, after 10 minutes of delay, execute the scripts in /etc/cron.weekly, and montly execute the scripts in /etc/cron.monthly”. The delay is a nice way to avoid that all the tasks are executed simultaneously overloading the machine.

Thus, can we replace cron with anacron and the job is done?

Well, not yet at 100%. For sure, you can see from the cron configuration above that cron itself performs a check on the anacron presence. If in the system is present anacron, cron kindly “yields” and skip the execution of tasks contained in the cron.* folders from the granularity of the day and beyond.

It remains a task to do. Starting anacron. And this, typycally, is made using…. cron! Yes, cron!.

So we say to cron to start one time per day anacron, and anacron will perform the jobs. With an always on machine, it works perfectly, but actually adds really few to the original cron.

We want also that in case of machine restart the tasks that are “expired” during the off time are performed.

Well, how to get that anacron is also executed at machine startup?

Typycally, in old linux distribution (like Ubuntu 14.04) another run of anacron was delegated to the event deamon, binding the triggering of anacron to the power up event. This combined with the cron scheduled triggering will make anacron our weapon of choice.

  • At machine startup, anacron is triggered. If the power down time was so long that some task needs execution, this will be done immediately
  • At scheduled time, anacron is triggered. If there are task to be executed (typycally there are, unless we’re in the same day of the machine power on), they are executed

And we’re safe and sound.

The systemd timers

One day, playing with my new Raspbian box, I came across something curious. The execution of the daily jobs does not happens  at 5 AM like the cron configuration says, but… at midnight, or on machine just powered on, as soon the hour is changed.

Because there is now another player in job execution that can potentially replace both cron.

In newer distributions we can leverage on “service timers”. The system daemon is able to support scheduled configuration for execution of such services on some scheduling basis. And my anacron was pre-shipped to run in this mode, even if the legacy cron call is present, so let’s explore a bit how a service timer works.

Once again, I will not cover the complete syntax, there’s a plenty of articles on it, but the idea.

My anacron was triggered by this timer”

[Unit]
Description=Trigger anacron every hour

[Timer]
OnCalendar=hourly
RandomizedDelaySec=5m
Persistent=true

[Install]
WantedBy=timers.target

What the above means? Every hour, after a randomized delay of at least 5 minutes, the anacron daemon is triggered.

Is this covering any need?

Well, actually, yes. Considering the anacron behaviour, on a regularly powered on machine, the first execution of the day will do the check and the tasks if due, the other 23 are supposed to do nothing. But they are there, because if the machine is just powered on, within one hour from startup an anacron execution will be triggered.

In my opinion this is a bit redundant. I’m lazy, so lazy, that thinking that the dameon will be triggered 23 times more than the needed is too much :). No, seriously, I wanted to explore the potentiality of this triggering method.

First goal. Having it executed at a specified time of the day.

Well, this it’s quite simple. There’s a syntax very similar to the cron’s one, so has been enough to replace the OnCalendar=hourly with

OnCalendar=*-*-* 05:00:00

But, in this way I lost the “execution within one hour from the power on”.

Well, the systemd timers powerful also to achieve this second goal.

OnBootSec=15m

Added to our timer definitions, the above means “Run 15 minutes after boot”.

So that the final timer configuration is:

[Unit]
Description=Trigger anacron every hour

[Timer]
OnCalendar=*-*-* 05:00:00
OnBootSec=15m
RandomizedDelaySec=5m
Persistent=true

[Install]
WantedBy=timers.target

Ops, as my usual, I’ve updated the “code” but not the comments on it!

Note the Persistent=true  clause, that is essential to get the executions “persistent” across restarts, that is crucial to have the complete behaviour.

Playing with timers

Timers are stored in configuration files. So changing them is like editing those files. Unless for a detail: the timers are cached by the system daemon, so once a timer is edited on file, you need to ask the daemon to reload it (usually you’ll be warned of this by performing a reload of them, or a restart of the service.

It’s always possible to know from systemd the plan of the active timers issuing systemctl list-timers
:

NEXT                          LEFT     LAST                          PASSED  UNIT                         ACTIVATES
...

Tue 2018-07-10 05:02:08 CEST  15h left Mon 2018-07-09 05:02:21 CEST  8h ago  anacron.timer                anacron.service

...

That will list last and next supposed execution of the active timers, confirming your edits are working.

Are timers goint to replace cron/anacron?

Potentially many periodical tasks triggered by the pair cron/anacron may be moved in the form of a system “service”, triggered by a “timer” such the above. So probably, in time, this will happens.

Cron, Anacron and the system.d timers
Tag: