Skip to content

Commit

Frequent schedules run on a five-minute mark only inside their own hours, days and months; events' index moves to 0008 (events 0008)

syntaqxcommitted Parent0ef7289Browse files
4 files+34−160/4 viewed
+1−1
9696
9797 | Rule | What happens |
9898 | --- | --- |
99−| Every 5 minutes at most | A schedule more frequent than every 5 minutes, such as `* * * * *` or `*/2 * * * *`, runs every 5 minutes instead, on the five-minute marks (:00, :05, :10 and so on). |
99+| Every 5 minutes at most | A schedule more frequent than every 5 minutes, such as `* * * * *` or `*/2 * * * *`, runs every 5 minutes instead, on the five-minute marks (:00, :05, :10 and so on), at each mark that ends five minutes in which it would have run. A mark outside the hours, days or months the schedule names never runs: `* 9 * * *` runs from 09:00 to 09:55. |
100100 | No push for 60 days | Schedules pause in a repository that has had no push for 60 days. The next push to any branch resumes them. Other events and `workflow_dispatch` still start the workflow. |
101101 | Actions not paid for | When a scheduled run's job could not start because the workspace's plan, spend limit or the open-source pool does not cover it, that run fails and says why, and the workflow's schedule waits an hour before it tries again. |
102102 | Archived repository | Schedules wait until the repository is unarchived. |
+28−10
8585
8686 /// Whether it fires in the minute starting at `ms` since the epoch, UTC.
8787 pub fn fires_at(&self, ms: u64) -> bool {
88+ self.minutes[(ms / 60_000 % 60) as usize] && self.hour_and_date_at(ms)
89+ }
90+
91+ /// Whether the minute starting at `ms` is in its hours, days and
92+ /// months, whatever its minute field says.
93+ fn hour_and_date_at(&self, ms: u64) -> bool {
8894 let minutes_total = ms / 60_000;
89− let minute = (minutes_total % 60) as usize;
9095 let hour = (minutes_total / 60 % 24) as usize;
9196 let days_since_epoch = (minutes_total / 60 / 24) as i64;
9297 // 1970-01-01 was a Thursday.
98103 (true, true) => day_ok || weekday_ok,
99104 _ => day_ok && weekday_ok,
100105 };
101− self.minutes[minute] && self.hours[hour] && self.months[month as usize] && date_ok
106+ self.hours[hour] && self.months[month as usize] && date_ok
102107 }
103108
104109 /// Whether its minutes come closer together than
113118 /// Whether a workflow on this schedule runs in the minute starting at
114119 /// `ms`. A schedule no more frequent than every
115120 /// [`MIN_INTERVAL_MINUTES`] runs when it fires. A more frequent one
116− /// runs on the five-minute marks, at each one it fired at or since the
117− /// last: at most every five minutes.
121+ /// runs on the five-minute marks: at a mark that is itself in its
122+ /// hours, days and months, when it fired in the five minutes the mark
123+ /// ends. At most every five minutes, and never outside its own hours.
118124 pub fn runs_at(&self, ms: u64) -> bool {
119125 if !self.too_frequent() {
120126 return self.fires_at(ms);
121127 }
122128 let minute = ms / 60_000;
123129 minute % MIN_INTERVAL_MINUTES == 0
130+ && self.hour_and_date_at(ms)
124131 && (0..MIN_INTERVAL_MINUTES).any(|back| minute >= back && self.fires_at((minute - back) * 60_000))
125132 }
126133 }
210217 // Close across the hour counts too.
211218 assert!(Schedule::parse("2,58 * * * *").unwrap().too_frequent());
212219 assert!(!Schedule::parse("0 9 * * mon").unwrap().too_frequent());
213− // Every minute of one hour: the marks of that hour, and the one
214− // that closes it.
220+ // Every minute of one hour: its own marks, 09:00 to 09:55, and
221+ // nothing after.
215222 let nine = Schedule::parse("* 9 * * *").unwrap();
216− assert!(nine.runs_at(at(MONDAY, 9, 0)));
217− assert!(!nine.runs_at(at(MONDAY, 9, 1)));
218− assert!(nine.runs_at(at(MONDAY, 10, 0)));
219− assert!(!nine.runs_at(at(MONDAY, 10, 5)));
223+ let day: Vec<(u64, u64)> =
224+ (0..24 * 60).filter(|&m| nine.runs_at(at(MONDAY, m / 60, m % 60))).map(|m| (m / 60, m % 60)).collect();
225+ assert_eq!(day, (0..12).map(|i| (9, i * 5)).collect::<Vec<_>>());
226+ assert!(!nine.runs_at(at(MONDAY, 10, 0)));
227+ // Every minute of Mondays: the last run is 23:55, none on Tuesday.
228+ let mondays = Schedule::parse("*/1 * * * 1").unwrap();
229+ assert!(mondays.runs_at(at(MONDAY, 0, 0)));
230+ assert!(mondays.runs_at(at(MONDAY, 23, 55)));
231+ assert!(!mondays.runs_at(at(MONDAY + 1, 0, 0)));
232+ assert!(!(0..24 * 60).any(|m| mondays.runs_at(at(MONDAY + 1, m / 60, m % 60))));
233+ // Close minutes: one run for each window they fall in, at its mark.
234+ let close = Schedule::parse("0,3 * * * *").unwrap();
235+ let hour: Vec<u64> = (0..60).filter(|&m| close.runs_at(at(MONDAY, 3, m))).collect();
236+ assert_eq!(hour, [0, 5]);
237+ assert!(close.runs_at(at(MONDAY, 4, 0)));
220238 }
221239
222240 #[test]
+0−5
1−-- A repository's events of some types, newest first: the home page's pushes,
2−-- the overview's feed and the Activity tab. By repository, type and id
3−-- those are ranges, rather than a walk back through every event the
4−-- repository ever had.
5−CREATE INDEX IF NOT EXISTS events_repo_type ON events (repo_id, type, id);
+5−0
1+-- A repository's events of some types, newest first: the home page's pushes,
2+-- the overview's feed and the Activity tab. By repository, type and id
3+-- those are ranges, rather than a walk back through every event the
4+-- repository ever had.
5+CREATE INDEX IF NOT EXISTS events_repo_type ON events (repo_id, type, id);