Skip to content

Commit 35fc40b

Browse files
committed
fix: avoid queue starvation during retries
1 parent aac537f commit 35fc40b

3 files changed

Lines changed: 29 additions & 13 deletions

File tree

functions/src/api/retryBuild.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { CiBuilds } from '../model/ciBuilds';
55
import { CiJobs } from '../model/ciJobs';
66
import { Ingeminator } from '../logic/buildQueue/ingeminator';
77
import { GitHub } from '../service/github';
8-
import { RepoVersionInfo } from '../model/repoVersionInfo';
98
import { Discord } from '../service/discord';
109
import { defineSecret } from 'firebase-functions/params';
1110

@@ -79,8 +78,7 @@ export const retryBuild = onRequest(
7978

8079
// Schedule new build
8180
const gitHubClient = await GitHub.init(githubPrivateKey.value(), githubClientSecret.value());
82-
const repoVersionInfo = await RepoVersionInfo.getLatest();
83-
const scheduler = new Ingeminator(1, gitHubClient, repoVersionInfo);
81+
const scheduler = new Ingeminator(1, gitHubClient);
8482
const scheduledSuccessfully = await scheduler.rescheduleBuild(jobId, job, buildId, build);
8583

8684
// Report result

functions/src/logic/buildQueue/ingeminator.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { CiBuild, CiBuilds } from '../../model/ciBuilds';
33
import { EditorVersionInfo } from '../../model/editorVersionInfo';
44
import { Discord } from '../../service/discord';
55
import { Octokit } from '@octokit/rest';
6-
import { RepoVersionInfo } from '../../model/repoVersionInfo';
76
import { Scheduler } from './scheduler';
87
import admin from 'firebase-admin';
98
import Timestamp = admin.firestore.Timestamp;
@@ -15,15 +14,14 @@ import { logger } from 'firebase-functions/v2';
1514
export class Ingeminator {
1615
numberToSchedule: number;
1716
gitHubClient: Octokit;
18-
repoVersionInfo: RepoVersionInfo;
17+
private scheduledBuilds = 0;
1918

20-
constructor(numberToSchedule: number, gitHubClient: Octokit, repoVersionInfo: RepoVersionInfo) {
19+
constructor(numberToSchedule: number, gitHubClient: Octokit) {
2120
this.numberToSchedule = numberToSchedule;
2221
this.gitHubClient = gitHubClient;
23-
this.repoVersionInfo = repoVersionInfo;
2422
}
2523

26-
async rescheduleFailedJobs(jobs: CiJobQueue) {
24+
async rescheduleFailedJobs(jobs: CiJobQueue): Promise<number> {
2725
if (jobs.length <= 0) {
2826
throw new Error(
2927
'[Ingeminator] Expected ingeminator to be called with jobs to retry, none were given.',
@@ -39,6 +37,8 @@ export class Ingeminator {
3937

4038
await this.rescheduleFailedBuildsForJob(job);
4139
}
40+
41+
return this.scheduledBuilds;
4242
}
4343

4444
private async rescheduleFailedBuildsForJob(job: CiJobQueueItem) {
@@ -101,6 +101,7 @@ export class Ingeminator {
101101
if (!(await this.rescheduleBuild(jobId, jobData, buildId, BuildData))) {
102102
return;
103103
}
104+
this.scheduledBuilds += 1;
104105
}
105106

106107
await CiJobs.markJobAsScheduled(jobId);
@@ -117,7 +118,7 @@ export class Ingeminator {
117118
const { baseOs, targetPlatform } = buildInfo;
118119

119120
// Info from repo
120-
const repoVersions = Scheduler.parseRepoVersions(this.repoVersionInfo);
121+
const repoVersions = Scheduler.parseRepoVersions(jobData.repoVersionInfo);
121122
const { repoVersionFull, repoVersionMinor, repoVersionMajor } = repoVersions;
122123

123124
// Send the retry request

functions/src/logic/buildQueue/scheduler.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export class Scheduler {
1919
private _gitHub: Octokit | undefined;
2020
private maxConcurrentJobs: number;
2121
private repoVersionInfo: RepoVersionInfo;
22+
private reservedRetrySlots = 0;
2223

2324
private get gitHub(): Octokit {
2425
// @ts-ignore
@@ -166,6 +167,7 @@ export class Scheduler {
166167
async ensureThereAreNoFailedJobs(): Promise<boolean> {
167168
const { maxToleratedFailures, maxExtraJobsForRescheduling } = settings;
168169
const failingJobs = await CiJobs.getFailingJobsQueue();
170+
this.reservedRetrySlots = 0;
169171

170172
if (failingJobs.length >= 1) {
171173
const openSpots = await this.determineOpenSpots();
@@ -176,15 +178,24 @@ export class Scheduler {
176178
return false;
177179
}
178180

179-
const ingeminator = new Ingeminator(numberToReschedule, this.gitHub, this.repoVersionInfo);
180-
await ingeminator.rescheduleFailedJobs(failingJobs);
181+
const ingeminator = new Ingeminator(numberToReschedule, this.gitHub);
182+
const scheduledRetries = await ingeminator.rescheduleFailedJobs(failingJobs);
183+
this.reservedRetrySlots = Math.min(scheduledRetries, openSpots);
184+
185+
const remainingFreshSlots = openSpots - this.reservedRetrySlots;
186+
if (remainingFreshSlots > 0) {
187+
await Discord.sendDebug(
188+
`[Scheduler] Reserved ${this.reservedRetrySlots} slot(s) for retries and kept ${remainingFreshSlots} slot(s) available for fresh jobs.`,
189+
);
190+
}
181191
}
182192

183-
return failingJobs.length <= maxToleratedFailures;
193+
const openSpotsAfterRetries = await this.determineOpenSpotsForFreshJobs();
194+
return failingJobs.length <= maxToleratedFailures || openSpotsAfterRetries > 0;
184195
}
185196

186197
async buildLatestEditorImages(): Promise<boolean> {
187-
const openSpots = await this.determineOpenSpots();
198+
const openSpots = await this.determineOpenSpotsForFreshJobs();
188199
if (openSpots <= 0) {
189200
await Discord.sendDebug('[Scheduler] Not scheduling any new jobs, as the queue is full');
190201
return false;
@@ -246,4 +257,10 @@ export class Scheduler {
246257
const openSpots = this.maxConcurrentJobs - currentlyRunningJobs;
247258
return openSpots <= 0 ? 0 : openSpots;
248259
}
260+
261+
private async determineOpenSpotsForFreshJobs(): Promise<number> {
262+
const openSpots = await this.determineOpenSpots();
263+
const availableSpots = openSpots - this.reservedRetrySlots;
264+
return availableSpots <= 0 ? 0 : availableSpots;
265+
}
249266
}

0 commit comments

Comments
 (0)