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 isType=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 ofoneshot
(per the docs).