@@ -133,95 +133,6 @@ class PackageState extends db.ExpandoModel<String> {
133133 @db .DateTimeProperty (required : true , indexed: true )
134134 DateTime finished = initialTimestamp;
135135
136- /// Derive [pendingAt] using [versions] and [lastDependencyChanged] .
137- ///
138- /// When updating PackageState the pendingAt property is set to the minimum of:
139- /// * `scheduled + 31 days` for any version,
140- /// * `scheduled + 24 hours` for any version where `dependencyChanged > scheduled`
141- /// * `scheduled + 3 hours * attempts^2` for any version where `attempts > 0 && attempts < 3` .
142- void derivePendingAt () {
143- final versionStates = versions! .values;
144- pendingAt = [
145- // scheduled + 31 days
146- ...versionStates.map ((v) => v.scheduled.add (taskRetriggerInterval)),
147- // scheduled + 24 hours, where scheduled < lastDependencyChanged
148- ...versionStates
149- .where ((v) => v.scheduled.isBefore (lastDependencyChanged! ))
150- .map ((v) => v.scheduled.add (taskDependencyRetriggerCoolOff)),
151- // scheduled + 3 hours * attempts^2, where attempts > 0 && attempts < 3
152- ...versionStates
153- .where ((v) => v.attempts > 0 && v.attempts < taskRetryLimit)
154- .map ((v) => v.scheduled.add (taskRetryDelay (v.attempts))),
155- // Pick the minimum of the candidates, default scheduling in year 3k
156- // if there is no date before that.
157- ].fold (DateTime (3000 ), (a, b) => a! .isBefore (b) ? a : b);
158- }
159-
160- /// Return a list of pending versions for this package.
161- ///
162- /// When scheduling analysis of a package we piggyback along versions that
163- /// are going to be pending soon too. Hence, we return a version if:
164- /// * `now - scheduled > 21 days` ,
165- /// * `lastDependencyChanged > scheduled` , or,
166- /// * `attempts > 0 && attempts < 3 && now - scheduled > 3 hours * attempts^2`
167- List <String > pendingVersions ({DateTime ? at}) {
168- final at_ = at ?? clock.now ();
169- Duration timeSince (DateTime past) => at_.difference (past);
170-
171- final list = versions! .entries
172- .where (
173- // NOTE: Any changes here must be reflected in [derivePendingAt]
174- (e) =>
175- // If scheduled more than 21 days ago
176- timeSince (e.value.scheduled) > minTaskRetriggerInterval ||
177- // If a dependency has changed since it was last scheduled
178- lastDependencyChanged! .isAfter (e.value.scheduled) ||
179- // If:
180- // - attempts > 0 (analysis is not done, and has been started)
181- // - no more than 3 attempts have been done,
182- // - now - scheduled > 3 hours * attempts^2
183- (e.value.attempts > 0 &&
184- e.value.attempts < taskRetryLimit &&
185- timeSince (e.value.scheduled) >
186- taskRetryDelay (e.value.attempts)),
187- )
188- .map ((e) => e.key)
189- .map (Version .parse)
190- .toList ();
191-
192- // Prioritize stable versions first, prereleases after them (in decreasing order), e.g.
193- // - 2.5.0
194- // - 2.4.0
195- // - 2.0.0
196- // - 1.2.0
197- // - 3.0.0-dev2
198- // - 3.0.0-dev1
199- // - 2.7.0-beta
200- // - 1.0.0-dev
201- list.sort ((a, b) => compareSemanticVersionsDesc (a, b, true , true ));
202-
203- // Promote the first prerelease version to the second position, e.g.
204- // - 2.5.0
205- // - 3.0.0-dev2
206- // - 2.4.0
207- // - 2.0.0
208- // - 1.2.0
209- // - 3.0.0-dev1
210- // - 2.7.0-beta
211- // - 1.0.0-dev
212- //
213- // (applicable only when the second position is a stable version)
214- if (list.length > 2 && ! list[1 ].isPreRelease) {
215- final firstPrereleaseIndex = list.indexWhere ((v) => v.isPreRelease);
216- if (firstPrereleaseIndex > 1 ) {
217- final v = list.removeAt (firstPrereleaseIndex);
218- list.insert (1 , v);
219- }
220- }
221-
222- return list.map ((s) => s.toString ()).toList ();
223- }
224-
225136 /// Returns true if the current [PackageState] instance is new, no version analysis
226137 /// has not completed yet (with neither success nor failure).
227138 bool get hasNeverFinished => finished == initialTimestamp;
@@ -242,6 +153,101 @@ class PackageState extends db.ExpandoModel<String> {
242153 '\n )' ;
243154}
244155
156+ /// Derive the `pendingAt` field using [versions] and [lastDependencyChanged] .
157+ ///
158+ /// When updating PackageState the pendingAt property is set to the minimum of:
159+ /// * `scheduled + 31 days` for any version,
160+ /// * `scheduled + 24 hours` for any version where `dependencyChanged > scheduled`
161+ /// * `scheduled + 3 hours * attempts^2` for any version where `attempts > 0 && attempts < 3` .
162+ DateTime derivePendingAt ({
163+ required Map <String , PackageVersionStateInfo > versions,
164+ required DateTime lastDependencyChanged,
165+ }) {
166+ return [
167+ // scheduled + 31 days
168+ ...versions.values.map ((v) => v.scheduled.add (taskRetriggerInterval)),
169+ // scheduled + 24 hours, where scheduled < lastDependencyChanged
170+ ...versions.values
171+ .where ((v) => v.scheduled.isBefore (lastDependencyChanged))
172+ .map ((v) => v.scheduled.add (taskDependencyRetriggerCoolOff)),
173+ // scheduled + 3 hours * attempts^2, where attempts > 0 && attempts < 3
174+ ...versions.values
175+ .where ((v) => v.attempts > 0 && v.attempts < taskRetryLimit)
176+ .map ((v) => v.scheduled.add (taskRetryDelay (v.attempts))),
177+ // Pick the minimum of the candidates, default scheduling in year 3k
178+ // if there is no date before that.
179+ ].fold (DateTime (3000 ), (a, b) => a.isBefore (b) ? a : b);
180+ }
181+
182+ /// Return a list of pending versions for this package.
183+ ///
184+ /// When scheduling analysis of a package we piggyback along versions that
185+ /// are going to be pending soon too. Hence, we return a version if:
186+ /// * `now - scheduled > 21 days` ,
187+ /// * `lastDependencyChanged > scheduled` , or,
188+ /// * `attempts > 0 && attempts < 3 && now - scheduled > 3 hours * attempts^2`
189+ List <String > derivePendingVersions ({
190+ required Map <String , PackageVersionStateInfo > versions,
191+ required DateTime lastDependencyChanged,
192+ required DateTime ? at,
193+ }) {
194+ final at_ = at ?? clock.now ();
195+ Duration timeSince (DateTime past) => at_.difference (past);
196+
197+ final list = versions.entries
198+ .where (
199+ // NOTE: Any changes here must be reflected in [derivePendingAt]
200+ (e) =>
201+ // If scheduled more than 21 days ago
202+ timeSince (e.value.scheduled) > minTaskRetriggerInterval ||
203+ // If a dependency has changed since it was last scheduled
204+ lastDependencyChanged.isAfter (e.value.scheduled) ||
205+ // If:
206+ // - attempts > 0 (analysis is not done, and has been started)
207+ // - no more than 3 attempts have been done,
208+ // - now - scheduled > 3 hours * attempts^2
209+ (e.value.attempts > 0 &&
210+ e.value.attempts < taskRetryLimit &&
211+ timeSince (e.value.scheduled) >
212+ taskRetryDelay (e.value.attempts)),
213+ )
214+ .map ((e) => e.key)
215+ .map (Version .parse)
216+ .toList ();
217+
218+ // Prioritize stable versions first, prereleases after them (in decreasing order), e.g.
219+ // - 2.5.0
220+ // - 2.4.0
221+ // - 2.0.0
222+ // - 1.2.0
223+ // - 3.0.0-dev2
224+ // - 3.0.0-dev1
225+ // - 2.7.0-beta
226+ // - 1.0.0-dev
227+ list.sort ((a, b) => compareSemanticVersionsDesc (a, b, true , true ));
228+
229+ // Promote the first prerelease version to the second position, e.g.
230+ // - 2.5.0
231+ // - 3.0.0-dev2
232+ // - 2.4.0
233+ // - 2.0.0
234+ // - 1.2.0
235+ // - 3.0.0-dev1
236+ // - 2.7.0-beta
237+ // - 1.0.0-dev
238+ //
239+ // (applicable only when the second position is a stable version)
240+ if (list.length > 2 && ! list[1 ].isPreRelease) {
241+ final firstPrereleaseIndex = list.indexWhere ((v) => v.isPreRelease);
242+ if (firstPrereleaseIndex > 1 ) {
243+ final v = list.removeAt (firstPrereleaseIndex);
244+ list.insert (1 , v);
245+ }
246+ }
247+
248+ return list.map ((s) => s.toString ()).toList ();
249+ }
250+
245251/// State of a given `version` within a [PackageState] .
246252@JsonSerializable ()
247253class PackageVersionStateInfo {
0 commit comments