Skip to content

Commit 01e2338

Browse files
committed
Set ratelimited prebuild to aborted
1 parent 07b9858 commit 01e2338

File tree

3 files changed

+36
-43
lines changed

3 files changed

+36
-43
lines changed

components/server/ee/src/prebuilds/prebuild-manager.ts

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ import { DBWithTracing, TracedWorkspaceDB, WorkspaceDB } from '@gitpod/gitpod-db
88
import { CommitContext, Project, ProjectEnvVar, StartPrebuildContext, StartPrebuildResult, TaskConfig, User, WorkspaceConfig, WorkspaceInstance } from '@gitpod/gitpod-protocol';
99
import { log } from '@gitpod/gitpod-protocol/lib/util/logging';
1010
import { TraceContext } from '@gitpod/gitpod-protocol/lib/util/tracing';
11-
import { inject, injectable } from 'inversify';
12-
import { URL } from 'url';
1311
import { HostContextProvider } from '../../../src/auth/host-context-provider';
1412
import { WorkspaceFactory } from '../../../src/workspace/workspace-factory';
1513
import { ConfigProvider } from '../../../src/workspace/config-provider';
@@ -18,6 +16,10 @@ import { Config } from '../../../src/config';
1816
import { ProjectsService } from '../../../src/projects/projects-service';
1917
import { secondsBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil';
2018

19+
import { inject, injectable } from 'inversify';
20+
import * as opentracing from 'opentracing';
21+
import { URL } from 'url';
22+
2123
export class WorkspaceRunningError extends Error {
2224
constructor(msg: string, public instance: WorkspaceInstance) {
2325
super(msg);
@@ -116,33 +118,32 @@ export class PrebuildManager {
116118
prebuildContext.commitHistory = await contextParser.fetchCommitHistory({ span }, user, contextURL, commit, maxDepth);
117119
}
118120

119-
const now = new Date()
120-
const runningCount = await this.workspaceDB.trace({span}).countUnabortedPrebuildsSince(cloneURL, new Date(secondsBefore(now.toISOString(), 300)))
121-
log.info("Created prebuild context with running count", prebuildContext, runningCount);
122-
if (runningCount > 10) {
123-
log.info("Would rate limit prebuild for ", cloneURL)
124-
await this.workspaceFactory.createAbortedWorkspace({span}, user, prebuildContext, contextURL)
125-
const id = await this.generateWorkspaceID(context);
126-
127-
await this.db.trace({span}).store(newWs);
128121

129-
}
130122

131123
const projectEnvVarsPromise = project ? this.projectService.getProjectEnvironmentVariables(project.id) : [];
132124
const workspace = await this.workspaceFactory.createForContext({span}, user, prebuildContext, contextURL);
133-
const prebuildPromise = this.workspaceDB.trace({span}).findPrebuildByWorkspaceID(workspace.id)!;
125+
const prebuild = await this.workspaceDB.trace({span}).findPrebuildByWorkspaceID(workspace.id)!;
126+
if (!prebuild) {
127+
throw new Error(`Failed to create a prebuild for: ${contextURL}`);
128+
}
134129

135-
// const canBuildNow = await this.prebuildRateLimiter.canBuildNow({ span }, user, cloneURL);
136-
// if (!canBuildNow) {
137-
// // we cannot start building now because the rate limiter prevents it.
138-
// span.setTag("starting", false);
139-
// return { wsid: workspace.id, done: false };;
140-
// }
130+
if (await this.shouldRateLimitPrebuild(span, cloneURL)) {
131+
prebuild.state = "aborted";
132+
prebuild.error = "Prebuild is rate limited. Please contact Gitpod if you believe this happened in error.";
133+
134+
await this.workspaceDB.trace({ span }).storePrebuiltWorkspace(prebuild)
135+
span.setTag("starting", false);
136+
span.setTag("ratelimited", true)
137+
return {
138+
wsid: workspace.id,
139+
prebuildId: prebuild.id,
140+
done: false,
141+
}
142+
}
141143

142144
span.setTag("starting", true);
143145
const projectEnvVars = await projectEnvVarsPromise;
144146
await this.workspaceStarter.startWorkspace({ span }, workspace, user, [], projectEnvVars, {excludeFeatureFlags: ['full_workspace_backup']});
145-
const prebuild = await prebuildPromise;
146147
if (!prebuild) {
147148
throw new Error(`Failed to create a prebuild for: ${contextURL}`);
148149
}
@@ -241,4 +242,10 @@ export class PrebuildManager {
241242
}
242243
return hostContext.contextParser;
243244
}
245+
246+
private async shouldRateLimitPrebuild(span: opentracing.Span, cloneURL: string): Promise<boolean> {
247+
const now = new Date()
248+
const runningCount = await this.workspaceDB.trace({span}).countUnabortedPrebuildsSince(cloneURL, new Date(secondsBefore(now.toISOString(), 300)))
249+
return runningCount > 10
250+
}
244251
}

components/server/src/workspace/config-provider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,4 +372,4 @@ export namespace InvalidGitpodYMLError {
372372
export function is(obj: object): obj is InvalidGitpodYMLError {
373373
return 'errorType' in obj && (obj as any).errorType === "invalidGitpodYML" && 'validationErrors' in obj;
374374
}
375-
}
375+
}

components/server/src/workspace/workspace-factory.ts

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@
44
* See License-AGPL.txt in the project root for license information.
55
*/
66

7-
import * as opentracing from 'opentracing';
87
import { DBWithTracing, TracedWorkspaceDB, WorkspaceDB, ProjectDB, TeamDB } from '@gitpod/gitpod-db/lib';
9-
import { AdditionalContentContext, CommitContext, IssueContext, PrebuiltWorkspaceContext, StartPrebuildContext, PullRequestContext, Repository, SnapshotContext, User, Workspace, WorkspaceConfig, WorkspaceContext, WorkspaceProbeContext, WorkspaceType } from '@gitpod/gitpod-protocol';
8+
import { AdditionalContentContext, CommitContext, IssueContext, PrebuiltWorkspaceContext, PullRequestContext, Repository, SnapshotContext, User, Workspace, WorkspaceConfig, WorkspaceContext, WorkspaceProbeContext } from '@gitpod/gitpod-protocol';
109
import { ErrorCodes } from '@gitpod/gitpod-protocol/lib/messaging/error';
1110
import { generateWorkspaceID } from '@gitpod/gitpod-protocol/lib/util/generate-workspace-id';
1211
import { log } from '@gitpod/gitpod-protocol/lib/util/logging';
@@ -135,8 +134,11 @@ export class WorkspaceFactory {
135134
}
136135
}
137136

138-
private async buildWorkspaceFromContext(span: opentracing.Span, ctx: TraceContext, user: User, context: CommitContext, normalizedContextURL: string, workspaceType: WorkspaceType): Promise<Workspace> {
139-
// TODO(janx): We potentially fetch the same Project twice in this flow (once here, and once in `configProvider`,
137+
protected async createForCommit(ctx: TraceContext, user: User, context: CommitContext, normalizedContextURL: string) {
138+
const span = TraceContext.startSpan("createForCommit", ctx);
139+
140+
try {
141+
// TODO(janx): We potentially fetch the same Project twice in this flow (once here, and once in `configProvider`,
140142
// to parse a potential custom config from the Project DB). It would be cool to fetch the Project only once (and
141143
// e.g. pass it to `configProvider.fetchConfig` here).
142144
const [ { config, literalConfig} , project ] = await Promise.all([
@@ -169,9 +171,9 @@ export class WorkspaceFactory {
169171
}
170172

171173
const id = await this.generateWorkspaceID(context);
172-
return {
174+
const newWs: Workspace = {
173175
id,
174-
type: workspaceType,
176+
type: "regular",
175177
creationTime: new Date().toISOString(),
176178
contextURL: normalizedContextURL,
177179
projectId,
@@ -181,13 +183,6 @@ export class WorkspaceFactory {
181183
imageSource,
182184
config
183185
};
184-
}
185-
186-
protected async createForCommit(ctx: TraceContext, user: User, context: CommitContext, normalizedContextURL: string) {
187-
const span = TraceContext.startSpan("createForCommit", ctx);
188-
189-
try {
190-
const newWs = await this.buildWorkspaceFromContext(span, ctx, user, context, normalizedContextURL, "regular")
191186
await this.db.trace({span}).store(newWs);
192187
return newWs;
193188
} catch (e) {
@@ -228,13 +223,4 @@ export class WorkspaceFactory {
228223
return await generateWorkspaceID();
229224
}
230225

231-
public async createAbortedWorkspace(ctx: TraceContext, user: User, context: StartPrebuildContext, normalizedContextURL: string): Promise<Workspace> {
232-
const span = TraceContext.startSpan("createForCommit", ctx);
233-
234-
const newWs = await this.buildWorkspaceFromContext(span, ctx, user, context, normalizedContextURL, "aborted")
235-
236-
await this.db.trace({span}).store(newWs);
237-
return newWs;
238-
}
239-
240226
}

0 commit comments

Comments
 (0)