Crontab supports things like "*/20" in the minutes column to run
every 20 minutes.  For example, given:

*/20 * * * * echo I am right on time

The job above would run at 0, 20, and 40 minutes of every hours.

job@ asked whether we could support a random offset so that jobs
would not always start at the same time, but still use the same
period (in this example every 20 minutes).  This turns out to
be fairly simple.

Below is a small diff to support step intervals with a random offset.
The syntax adds a '~' after the step value.  For example:

*/~20 * * * * echo mix it up a bit

will still run every 20 minutes but the initial run will be some
time between 0-19 minutes after the hour (inclusive).  Like the
existing random support, the starting offset for an entry is chosen
when the crontab file is first loaded and remains the same unless
the crontab file is modified (and reloaded).

The man page bits are from job@

Opinions?  Does the proposed syntax seem OK?

 - todd

Index: usr.sbin/cron/crontab.5
===================================================================
RCS file: /cvs/src/usr.sbin/cron/crontab.5,v
retrieving revision 1.41
diff -u -p -u -r1.41 crontab.5
--- usr.sbin/cron/crontab.5     18 Apr 2020 17:11:40 -0000      1.41
+++ usr.sbin/cron/crontab.5     3 May 2023 20:17:31 -0000
@@ -174,6 +174,15 @@ Steps are also permitted after an asteri
 just use
 .Dq */2 .
 .Pp
+A step value may be preceded with a
+.Ql ~
+character to specify a one-off random wait period before the step cycle begins.
+For example, to avoid a thundering herd at the top and bottom of the hour,
+.Dq */~30
+can be used in the
+.Ar minute
+field to specify command execution happen twice an hour at consistent 
intervals.
+.Pp
 An asterisk
 .Pq Ql *
 is short form for a range of all allowed values.
Index: usr.sbin/cron/entry.c
===================================================================
RCS file: /cvs/src/usr.sbin/cron/entry.c,v
retrieving revision 1.53
diff -u -p -u -r1.53 entry.c
--- usr.sbin/cron/entry.c       21 May 2022 01:21:29 -0000      1.53
+++ usr.sbin/cron/entry.c       3 May 2023 17:24:47 -0000
@@ -524,12 +524,22 @@ get_range(bitstr_t *bits, int low, int h
        /* check for step size
         */
        if (ch == '/') {
+               int rndstep = 0;
+
                /* eat the slash
                 */
                ch = get_char(file);
                if (ch == EOF)
                        return (EOF);
 
+               /* check for random step size offset. */
+               if (ch == '~') {
+                   rndstep = 1;
+                   ch = get_char(file);
+                   if (ch == EOF)
+                           return (EOF);
+               }
+
                /* get the step size -- note: we don't pass the
                 * names here, because the number is not an
                 * element id, it's a step size.  'low' is
@@ -538,6 +548,11 @@ get_range(bitstr_t *bits, int low, int h
                ch = get_number(&num3, 0, NULL, ch, file, ", \t\n");
                if (ch == EOF || num3 == 0)
                        return (EOF);
+
+               if (rndstep) {
+                       /* add a random offset less than the step size */
+                       num1 += arc4random_uniform(num3);
+               }
        } else {
                /* no step.  default==1.
                 */

Reply via email to