Skip to content

Commit 3b89aac

Browse files
committed
[dashboard] Start at most one workspace after prebuild finished
Under certain circumstances - if a workspace failure appeared before the ideURL got set - we would never leave the HeadlessLogView but start a new workspace over and over again. As we do not handle this error well, it would only get caught by timeouts (1h), leading to users being effectively blocked because they reached their parallel workspace limit
1 parent 967a060 commit 3b89aac

File tree

3 files changed

+27
-10
lines changed

3 files changed

+27
-10
lines changed

components/dashboard/src/start/CreateWorkspace.tsx

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -330,19 +330,32 @@ function RunningPrebuildView(props: RunningPrebuildViewProps) {
330330
const logsEmitter = new EventEmitter();
331331
const service = getGitpodService();
332332
let pollTimeout: NodeJS.Timeout | undefined;
333+
let prebuildDoneTriggered: boolean = false;
333334

334335
useEffect(() => {
335-
const pollIsPrebuildDone = async () => {
336-
clearTimeout(pollTimeout!);
337-
const available = await service.server.isPrebuildDone(props.runningPrebuild.prebuildID);
338-
if (available) {
336+
const checkIsPrebuildDone = async (): Promise<boolean> => {
337+
if (prebuildDoneTriggered) {
338+
console.debug("prebuild done already triggered, doing nothing");
339+
return true;
340+
}
341+
342+
const done = await service.server.isPrebuildDone(props.runningPrebuild.prebuildID);
343+
if (done) {
344+
// note: this treats "done" as "available" which is not equivalent.
345+
// This works because the backend ignores prebuilds which are not "available", and happily starts a workspace as if there was no prebuild at all.
346+
prebuildDoneTriggered = true;
339347
props.onPrebuildSucceeded();
340-
return;
348+
return true;
341349
}
350+
return false;
351+
};
352+
const pollIsPrebuildDone = async () => {
353+
clearTimeout(pollTimeout!);
354+
await checkIsPrebuildDone();
342355
pollTimeout = setTimeout(pollIsPrebuildDone, 10000);
343356
};
344357

345-
const disposables = watchHeadlessLogs(service.server, props.runningPrebuild.instanceID, (chunk) => logsEmitter.emit('logs', chunk), pollIsPrebuildDone);
358+
const disposables = watchHeadlessLogs(service.server, props.runningPrebuild.instanceID, (chunk) => logsEmitter.emit('logs', chunk), checkIsPrebuildDone);
346359
return function cleanup() {
347360
clearTimeout(pollTimeout!);
348361
disposables.dispose();

components/dashboard/src/start/StartWorkspace.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ function HeadlessWorkspaceView(props: { instanceId: string }) {
346346

347347
useEffect(() => {
348348
const service = getGitpodService();
349-
const disposables = watchHeadlessLogs(service.server, props.instanceId, (chunk) => logsEmitter.emit('logs', chunk), () => {});
349+
const disposables = watchHeadlessLogs(service.server, props.instanceId, (chunk) => logsEmitter.emit('logs', chunk), async () => { return false; });
350350
return function cleanup() {
351351
disposables.dispose();
352352
};

components/dashboard/src/start/WorkspaceLogs.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,13 @@ export default class WorkspaceLogs extends React.Component<WorkspaceLogsProps, W
8787
}
8888
}
8989

90-
export function watchHeadlessLogs(server: GitpodServer, instanceId: string, onLog: (chunk: string) => void, checkIsDone: () => Promise<void> | void): DisposableCollection {
90+
export function watchHeadlessLogs(server: GitpodServer, instanceId: string, onLog: (chunk: string) => void, checkIsDone: () => Promise<boolean>): DisposableCollection {
9191
const disposables = new DisposableCollection();
9292

9393
const startWatchingLogs = async () => {
94-
await checkIsDone();
94+
if (await checkIsDone()) {
95+
return;
96+
}
9597

9698
const retry = async (reason: string, err?: Error) => {
9799
console.debug("re-trying headless-logs because: " + reason, err);
@@ -155,7 +157,9 @@ export function watchHeadlessLogs(server: GitpodServer, instanceId: string, onLo
155157
}
156158
reader.cancel()
157159

158-
await checkIsDone();
160+
if (await checkIsDone()) {
161+
return;
162+
}
159163
} catch(err) {
160164
reader?.cancel().catch(console.debug);
161165
await retry("error while listening to stream", err);

0 commit comments

Comments
 (0)