提交 a941338b 编写于 作者: J Jesse Glick

Promote use of crontab hashes in documentation and form validation.

上级 b8151b57
......@@ -31,6 +31,7 @@ import java.util.GregorianCalendar;
import java.util.Locale;
import static java.util.Calendar.*;
import javax.annotation.CheckForNull;
/**
* Table for driving scheduled tasks.
......@@ -412,21 +413,43 @@ public final class CronTab {
* but semantically suspicious combinations, like
* "* 0 * * *"
*/
public String checkSanity() {
for( int i=0; i<5; i++ ) {
public @CheckForNull String checkSanity() {
OUTER: for (int i = 0; i < 5; i++) {
long bitMask = (i<4)?bits[i]:(long)dayOfWeek;
for( int j=BaseParser.LOWER_BOUNDS[i]; j<=BaseParser.UPPER_BOUNDS[i]; j++ ) {
if(!checkBits(bitMask,j)) {
// this rank has a sparse entry.
// if we have a sparse rank, one of them better be the left-most.
if(i>0)
return Messages.CronTab_do_you_really_mean_every_minute_when_you(spec, "0 " + spec.substring(spec.indexOf(' ')+1));
return Messages.CronTab_do_you_really_mean_every_minute_when_you(spec, "H " + spec.substring(spec.indexOf(' ') + 1));
// once we find a sparse rank, upper ranks don't matter
return null;
break OUTER;
}
}
}
String hashified = hashify(spec);
if (hashified != null) {
return Messages.CronTab_spread_load_evenly_by_using_rather_than_(hashified, spec);
}
return null;
}
/**
* Checks a prospective crontab specification to see if it could benefit from balanced hashes.
* @param spec a (legal) spec
* @return a similar spec that uses a hash, if such a transformation is necessary; null if it is OK as is
* @since XXX
*/
public static @CheckForNull String hashify(String spec) {
if (spec.startsWith("*/")) {
return "H" + spec.substring(1);
} else if (spec.matches("\\d+ .+")) {
return "H " + spec.substring(spec.indexOf(' ') + 1);
} else {
return null;
}
}
}
......@@ -24,4 +24,5 @@ BaseParser.StartEndReversed=You mean {0}-{1}?
BaseParser.MustBePositive=step must be positive, but found {0}
BaseParser.OutOfRange={0} is an invalid value. Must be within {1} and {2}
CronTab.do_you_really_mean_every_minute_when_you=Do you really mean "every minute" when you say "{0}"? Perhaps you meant "{1}"
CronTab.spread_load_evenly_by_using_rather_than_=Spread load evenly by using \u2018{0}\u2019 rather than \u2018{1}\u2019
CronTabList.InvalidInput=Invalid input: "{0}": {1}
......@@ -5,23 +5,23 @@
<table>
<tr>
<td>MINUTE</td>
<td>Minutes within the hour (0-59)</td>
<td>Minutes within the hour (059)</td>
</tr>
<tr>
<td>HOUR</td>
<td>The hour of the day (0-23)</td>
<td>The hour of the day (023)</td>
</tr>
<tr>
<td>DOM</td>
<td>The day of the month (1-31)</td>
<td>The day of the month (131)</td>
</tr>
<tr>
<td>MONTH</td>
<td>The month (1-12)</td>
<td>The month (112)</td>
</tr>
<tr>
<td>DOW</td>
<td>The day of the week (0-7) where 0 and 7 are Sunday.</td>
<td>The day of the week (07) where 0 and 7 are Sunday.</td>
</tr>
</table>
<p>
......@@ -29,49 +29,47 @@
available. In the order of precedence,
</p>
<ul>
<li>'*' can be used to specify all valid values.</li>
<li>'M-N' can be used to specify a range, such as "1-5"</li>
<li>'M-N/X' or '*/X' can be used to specify skips of X's value through the range,
such as "*/15" in the MINUTE field for "0,15,30,45" and "1-6/2" for "1,3,5"</li>
<li>'A,B,...,Z' can be used to specify multiple values, such as "0,30" or "1,3,5"</li>
<li><code>*</code> specifies all valid values</li>
<li><code>M-N</code> specifies a range of values</li>
<li><code>M-N/X</code> or <code>*/X</code> steps by intervals of X through the specified range or whole valid range</li>
<li><code>A,B,...,Z</code> enumerates multiple values</li>
</ul>
<p>
To allow periodically scheduled tasks to produce even load on the system,
the '<tt>H</tt>' token can be used. For example, people often use
'<tt>0 0 * * *</tt>' for a daily job, but this ends up causing a large
spike in midnight. In contrast, doing '<tt>H H * * *</tt>' would
still execute a job once a day, but the actual time of the day this gets
executed will be spread over by Jenkins.
the symbol <code>H</code> (for “hash”) should be used wherever possible.
For example, using <code>0 0 * * *</code> for a dozen daily jobs
will cause a large spike at midnight.
In contrast, using <code>H H * * *</code> would still execute each job once a day,
but not all at the same time, better using limited resources.
</p><p>
The 'H' token can be used with a range. For example, '<tt>H H(0-7) * * *</tt>'
means some time between midnight to 7:59am.
You can also use skips with 'H', with or without ranges.
For example, '<tt>H/15 * * * *</tt>' means every fifteen minutes (perhaps at :07, :22, :37, :52);
'<tt>H(0-29)/10 * * * *</tt>' means every ten minutes in the first half of every hour (perhaps at :04, :14, :24).
The <code>H</code> symbol can be used with a range. For example, <code>H H(0-7) * * *</code>
means some time between 12:00 AM (midnight) to 7:59 AM.
You can also use step intervals with <code>H</code>, with or without ranges.
</p><p>
The '<tt>H</tt>' token can be thought of as a random value over a range,
The <code>H</code> symbol can be thought of as a random value over a range,
but it actually is a hash of the job name, not a random function, so that
the value remains stable for any given project.
</p>
<p>
Empty lines and lines that start with '#' will be ignored as comments.
Empty lines and lines that start with <code>#</code> will be ignored as comments.
</p><p>
In addition, '@yearly', '@annually', '@monthly', '@weekly', '@daily', '@midnight',
and '@hourly' are supported.
These use the hash system for automatic balancing; for example '@hourly' could mean at any time during the hour.
'@midnight' actually means some time between 12:00 AM and 2:59 AM.
In addition, <code>@yearly</code>, <code>@annually</code>, <code>@monthly</code>,
<code>@weekly</code>, <code>@daily</code>, <code>@midnight</code>,
and <code>@hourly</code> are supported as convenient aliases.
These use the hash system for automatic balancing.
For example, <code>@hourly</code> is the same as <code>H * * * *</code> and could mean at any time during the hour.
<code>@midnight</code> actually means some time between 12:00 AM and 2:59 AM.
</p><p>
Examples:
</p>
<table>
<tr>
<td>Examples</td>
<td>
<pre>
# every minute
* * * * *
# every 5 mins past the hour
5 * * * *
# every fifteen minutes (perhaps at :07, :22, :37, :52)
H/15 * * * *
# every ten minutes in the first half of every hour (three times, perhaps at :04, :14, :24)
H(0-29)/10 * * * *
# once every two hours every weekday (perhaps at 10:38 AM, 12:38 PM, 2:38 PM, 4:38 PM)
H 9-16/2 * * 1-5
# once a day on the 1st and 15th of every month except December
H H 1,15 1-11 *
</pre>
</td>
</tr>
</table>
</div>
\ No newline at end of file
</div>
......@@ -174,14 +174,19 @@ public class CronTabTest {
}
@Test public void checkSanity() throws Exception {
assertEquals(Messages.CronTab_do_you_really_mean_every_minute_when_you("* * * * *", "0 * * * *"), new CronTab("* * * * *").checkSanity());
assertEquals(null, new CronTab("0 * * * *").checkSanity());
assertEquals(null, new CronTab("0 3 * * *").checkSanity());
assertEquals(null, new CronTab("@hourly").checkSanity());
assertEquals(Messages.CronTab_do_you_really_mean_every_minute_when_you("* * * * *", "H * * * *"), new CronTab("* * * * *").checkSanity());
assertEquals(null, new CronTab("H H(0-2) * * *", Hash.from("stuff")).checkSanity());
assertEquals(Messages.CronTab_do_you_really_mean_every_minute_when_you("* 0 * * *", "0 0 * * *"), new CronTab("* 0 * * *").checkSanity());
assertEquals(Messages.CronTab_do_you_really_mean_every_minute_when_you("* 6,18 * * *", "0 6,18 * * *"), new CronTab("* 6,18 * * *").checkSanity());
assertEquals(Messages.CronTab_do_you_really_mean_every_minute_when_you("* 0 * * *", "H 0 * * *"), new CronTab("* 0 * * *").checkSanity());
assertEquals(Messages.CronTab_do_you_really_mean_every_minute_when_you("* 6,18 * * *", "H 6,18 * * *"), new CronTab("* 6,18 * * *").checkSanity());
// dubious; could be improved:
assertEquals(Messages.CronTab_do_you_really_mean_every_minute_when_you("* * 3 * *", "0 * 3 * *"), new CronTab("* * 3 * *").checkSanity());
assertEquals(Messages.CronTab_do_you_really_mean_every_minute_when_you("* * 3 * *", "H * 3 * *"), new CronTab("* * 3 * *").checkSanity());
// promote hashes:
assertEquals(Messages.CronTab_spread_load_evenly_by_using_rather_than_("H/15 * * * *", "*/15 * * * *"), new CronTab("*/15 * * * *").checkSanity());
// XXX 0,15,30,45 * * * * → H/15 * * * *
assertEquals(Messages.CronTab_spread_load_evenly_by_using_rather_than_("H * * * *", "0 * * * *"), new CronTab("0 * * * *").checkSanity());
// if the user specifically asked for 3:00 AM, probably we should stick to 3:00–3:59
assertEquals(Messages.CronTab_spread_load_evenly_by_using_rather_than_("H 3 * * *", "0 3 * * *"), new CronTab("0 3 * * *").checkSanity());
}
/**
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册