Search code examples
timersystemd

Only use systemd timer oncalendar events


I'm trying to get a service to run at :0/5:00 (every 5min) every time. This process may take more or less than 5min. If it passes the 5min mark, do nothing, and start the next one on the next 5min block.

This all works properly when the process takes less than 5min, but if the process takes more than 5min, it re-starts the service immediately.

From what I've read, the default behavior is to skip the next execution if the service time overflows (see https://unix.stackexchange.com/questions/203254/does-systemd-timer-unit-skip-the-next-run-if-the-process-hasnt-finished-yet). However, I also see a question which seems to indicate that the service will start as soon as the previous execution completes (see https://unix.stackexchange.com/questions/487562/how-to-make-systemd-timer-run-exactly-every-10-minutes-with-a-task-that-lasts-s) which fits with my expreiences.

(For testing purposes, I'm using :0/1:0 so that I don't have to wait 5min every time)

My timer:

[Unit]
Description=Start service on timed schedule.
After=network-online.target
Wants=network-online.target

[Timer]
OnCalendar=*:0/1:0
AccuracySec=1us

[Install]
WantedBy=timers.target

My service:

[Unit]
Description=Do a thing
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/bin/bash /usr/local/bin/my-script.sh

My script:

#!/bin/bash

PROG="/usr/local/bin/fake-third-party.sh"

exec $PROG &>> /tmp/timer_testing.out

Fake third-party script:

#!/bin/bash

echo "`date` I sleep now"

sleep 76

echo "`date` No more sleep"

So right now, when the 76s ends, it immediately starts the next one.

Thu Jun 25 13:01:00 UTC 2020 I sleep now
Thu Jun 25 13:02:16 UTC 2020 No more sleep
Thu Jun 25 13:02:16 UTC 2020 I sleep now
Thu Jun 25 13:03:32 UTC 2020 No more sleep
Thu Jun 25 13:03:32 UTC 2020 I sleep now

When what I'd be looking for is:

13:01:00 -> sleep
13:02:16 -> wake
13:03:00 -> sleep
13:04:16 -> wake
13:05:00 -> sleep

Solution

  • One possible (aka hacky) solution that is working right now is the following:

    Change the my-script.sh to have this at the front:

    minutes=$(date +"%-M" | tail -c 2)
    seconds=$(date +"%S")
    if [[ "$seconds" != "00" || ! ( "$minutes" == "0" || "$minutes" == "5" ) ]]; then
    then
      exit 0
    fi
    

    This does trigger the my-script.sh, but the script never calls exec third-party-script so, it accomplishes the requirement.

    However I would like to do this the 'proper' way, if there is one.