Systemd timer for a process that runs every 5 minutes, but may run longer than 5 minutes

systemd

I did a little searching and didn't see any similar questions.

I have a job that should run once every five minutes.

There should only be one job running at a time.

If the program takes more than 5 minutes to run, it should start again immediately, not wait.

Most of the time, the job will complete in under a minute, occasionally it must do things that might take as long as 20 minutes.

I currently run this job as a systemd service, and the job is a shell script that calls the actual program in a loop, and sleeps between iterations (5 minutes – last run time). It works great.

#!/bin/sh
#
# Sample executable repeat-loop
#
# Run my job in a loop.  If it takes less than max_delay seconds
# to run, sleep until it's time to repeat.  If, for some reason, it took longer, just start
# over again immediately.

max_delay=300                       # maximum delay between runs

# Don't allow multiple copies of this script to run
[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@"

restart=0
now=0

while true ; do
    now=`date -u '+%s'`
    if [ $restart -gt $now ] ; then
        sleep `expr $restart - $now`
    fi
    restart=`expr $now + ${max_delay}`

    echo "my job runs here and may take between 30 seconds and 20 minutes to execute"
done

Since I am a bloody idiot, I was thinking it would be great to change this to a oneshot service, and have systemd timers fire off.

I haven't been able to figure out how to emulate this behavior using OnUnitActiveSec or OnUnitInactiveSec

OnUnitActiveSec is going to fire up my oneshot service every x seconds.
OnUnitInactiveSec is going to fire up my oneshot x seconds after the last time the oneshot terminated.

Any thoughts besides "Hey, your shell script works great, why beat yourself with a dead horse?"

Best Answer

I should have tested better, rather than relying on my (incorrect) interpretation of the docs.

Creating a timer file and using OnUnitActiveSec pointing to a service file that is Type=simple does exactly what I want. The timer will be invoked every 5 minutes, if the process runs longer than 5 minutes, it will be invoked (almost) immediately. It will not be double-invoked.

It's important to use simple instead of oneshot (per the docs).

Related Topic