I have scheduled a cron job to run every minute but sometimes the script takes more than a minute to finish and I don't want the jobs to start "stacking up" over each other. I guess this is a concurrency problem - i.e. the script execution needs to be mutually exclusive.
To solve the problem I made the script look for the existence of a particular file ("lockfile.txt") and exit if it exists or touch
it if it doesn't. But this is a pretty lousy semaphore! Is there a best practice that I should know about? Should I have written a daemon instead?
There are a couple of programs that automate this feature, take away the annoyance and potential bugs from doing this yourself, and avoid the stale lock problem by using flock behind the scenes, too (which is a risk if you're just using touch). I've used
lockrun
andlckdo
in the past, but now there'sflock
(1) (in newish versions of util-linux) which is great. It's really easy to use:Best way in shell is to use flock(1)
Actually,
flock -n
may be used instead oflckdo
*, so you will be using code from kernel developers.Building on womble's example, you would write something like:
BTW, looking at the code, all of
flock
,lockrun
, andlckdo
do the exact same thing, so it's just a matter of which is most readily available to you.You havent specified if you want the script to wait for the previous run to complete or not. By "I don't want the jobs to start "stacking up" over each other", I guess you are implying that you want the script to exit if already running,
So, if you don want to depend on lckdo or similar, you can do this:
Now that systemd is out, there is another scheduling mechanism on Linux systems:
A
systemd.timer
In
/etc/systemd/system/myjob.service
or~/.config/systemd/user/myjob.service
:In
/etc/systemd/system/myjob.timer
or~/.config/systemd/user/myjob.timer
:If the service unit is already activating when the timer next activates, then another instance of the service will not be started.
An alternative, which starts the job once at boot and one minute after each run is finished:
You can use a lock file. Create this file when the script starts and delete it when it finishes. The script, before it runs its main routine, should check if the lock file exists and proceed accordingly.
Lockfiles are used by initscripts and by many other applications and utilities in Unix systems.
I would recommend using run-one command - much simpler than dealing with the locks. From the docs:
run-one is a wrapper script that runs no more than one unique instance of some command with a unique set of arguments. This is often useful with cronjobs, when you want no more than one copy running at a time.
run-this-one is exactly like run-one, except that it will use pgrep and kill to find and kill any running processes owned by the user and matching the target commands and arguments. Note that run-this-one will block while trying to kill matching processes, until all matching processes are dead.
run-one-constantly operates exactly like run-one except that it respawns "COMMAND [ARGS]" any time COMMAND exits (zero or non-zero).
keep-one-running is an alias for run-one-constantly.
run-one-until-success operates exactly like run-one-constantly except that it respawns "COMMAND [ARGS]" until COMMAND exits successfully (ie, exits zero).
run-one-until-failure operates exactly like run-one-constantly except that it respawns "COMMAND [ARGS]" until COMMAND exits with failure (ie, exits non-zero).
This might also be a sign that you're doing the wrong thing. If your jobs run that closely and that frequently, maybe you should consider de-cronning it and making it a daemon-style program.
Your cron daemon shouldn't be invoking jobs if previous instances of them are still running. I'm the developer of one cron daemon dcron, and we specifically try to prevent that. I don't know how Vixie cron or other daemons handle this.
I have created one jar to solve such issue like duplicate crons are running could be java or shell cron. Just pass cron name in Duplicates.CloseSessions("Demo.jar") this will search and kill existng pid for this cron except current. I have implemented method to do this stuff. String proname=ManagementFactory.getRuntimeMXBean().getName(); String pid=proname.split("@")[0]; System.out.println("Current PID:"+pid);
And then kill killid string with again shell command