Skip to content

Commit 499798f

Browse files
committed
Wrap shared process in retry
1 parent 5d02194 commit 499798f

5 files changed

Lines changed: 36 additions & 26 deletions

File tree

packages/ide/src/fill/notification.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,30 @@ export interface IProgressService {
4141
}
4242

4343
/**
44-
* Temporary notification service.
44+
* Console-based notification service.
4545
*/
4646
export class NotificationService implements INotificationService {
4747
public error(error: Error): void {
4848
logger.error(error.message, field("error", error));
4949
}
5050

51-
public prompt(_severity: Severity, message: string, _buttons: INotificationButton[], _onCancel: () => void): INotificationHandle {
52-
throw new Error(`cannot prompt using the console: ${message}`);
51+
public prompt(severity: Severity, message: string, _buttons: INotificationButton[], _onCancel: () => void): INotificationHandle {
52+
switch (severity) {
53+
case Severity.Info: logger.info(message); break;
54+
case Severity.Warning: logger.warn(message); break;
55+
case Severity.Error: logger.error(message); break;
56+
}
57+
58+
return {
59+
close: (): void => undefined,
60+
updateMessage: (): void => undefined,
61+
updateButtons: (): void => undefined,
62+
};
5363
}
5464
}
5565

5666
/**
57-
* Temporary progress service.
67+
* Console-based progress service.
5868
*/
5969
export class ProgressService implements IProgressService {
6070
public start<T>(title: string, task: (progress: IProgress) => Promise<T>): Promise<T> {

packages/ide/src/retry.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { logger } from "@coder/logger";
1+
import { logger, field } from "@coder/logger";
22
import { NotificationService, INotificationHandle, INotificationService, Severity } from "./fill/notification";
33

44
interface IRetryItem {
@@ -105,7 +105,7 @@ export class Retry {
105105
/**
106106
* Retry a service.
107107
*/
108-
public run(name: string): void {
108+
public run(name: string, error?: Error): void {
109109
if (!this.items.has(name)) {
110110
throw new Error(`"${name}" is not registered`);
111111
}
@@ -117,15 +117,15 @@ export class Retry {
117117

118118
item.running = true;
119119
// This timeout is for the case when the connection drops; this allows time
120-
// for the Wush service to come in and block everything because some other
120+
// for the socket service to come in and block everything because some other
121121
// services might make it here first and try to restart, which will fail.
122122
setTimeout(() => {
123123
if (this.blocked && this.blocked !== name) {
124124
return;
125125
}
126126

127127
if (!item.count || item.count < this.maxImmediateRetries) {
128-
return this.runItem(name, item);
128+
return this.runItem(name, item, error);
129129
}
130130

131131
if (!item.delay) {
@@ -137,10 +137,10 @@ export class Retry {
137137
}
138138
}
139139

140-
logger.info(`Retrying ${name.toLowerCase()} in ${item.delay}s`);
140+
logger.info(`Retrying ${name.toLowerCase()} in ${item.delay}s`, error && field("error", error.message));
141141
const itemDelayMs = item.delay * 1000;
142142
item.end = Date.now() + itemDelayMs;
143-
item.timeout = setTimeout(() => this.runItem(name, item), itemDelayMs);
143+
item.timeout = setTimeout(() => this.runItem(name, item, error), itemDelayMs);
144144

145145
this.updateNotification();
146146
}, this.waitDelay);
@@ -165,7 +165,7 @@ export class Retry {
165165
/**
166166
* Run an item.
167167
*/
168-
private runItem(name: string, item: IRetryItem): void {
168+
private runItem(name: string, item: IRetryItem, error?: Error): void {
169169
if (!item.count) {
170170
item.count = 1;
171171
} else {
@@ -175,7 +175,7 @@ export class Retry {
175175
const retryCountText = item.count <= this.maxImmediateRetries
176176
? `[${item.count}/${this.maxImmediateRetries}]`
177177
: `[${item.count}]`;
178-
logger.info(`Trying ${name.toLowerCase()} ${retryCountText}...`);
178+
logger.info(`Starting ${name.toLowerCase()} ${retryCountText}...`, error && field("error", error.message));
179179

180180
const endItem = (): void => {
181181
this.stopItem(item);

packages/logger/src/logger.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,10 @@ export class Time {
3838
) { }
3939
}
4040

41+
// `undefined` is allowed to make it easy to conditionally display a field.
42+
// For example: `error && field("error", error)`
4143
// tslint:disable-next-line no-any
42-
export type FieldArray = Array<Field<any>>;
44+
export type FieldArray = Array<Field<any> | undefined>;
4345

4446
// Functions can be used to remove the need to perform operations when the
4547
// logging level won't output the result anyway.
@@ -338,9 +340,9 @@ export class Logger {
338340
passedFields = values as FieldArray;
339341
}
340342

341-
const fields = this.defaultFields
342-
? passedFields.concat(this.defaultFields)
343-
: passedFields;
343+
const fields = (this.defaultFields
344+
? passedFields.filter((f) => !!f).concat(this.defaultFields)
345+
: passedFields.filter((f) => !!f)) as Array<Field<any>>; // tslint:disable-line no-any
344346

345347
const now = Date.now();
346348
let times: Array<Field<Time>> = [];

packages/server/src/cli.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ export class Entry extends Command {
9292
logger.info("Additional documentation: https://coder.com/docs");
9393
logger.info("Initializing", field("data-dir", dataDir), field("working-dir", workingDir), field("log-dir", logDir));
9494
const sharedProcess = new SharedProcess(dataDir, builtInExtensionsDir);
95-
logger.info("Starting shared process...", field("socket", sharedProcess.socketPath));
9695
const sendSharedProcessReady = (socket: WebSocket): void => {
9796
const active = new SharedProcessActiveMessage();
9897
active.setSocketPath(sharedProcess.socketPath);
@@ -102,12 +101,7 @@ export class Entry extends Command {
102101
socket.send(serverMessage.serializeBinary());
103102
};
104103
sharedProcess.onState((event) => {
105-
if (event.state === SharedProcessState.Stopped) {
106-
logger.error("Shared process stopped. Restarting...", field("error", event.error));
107-
} else if (event.state === SharedProcessState.Starting) {
108-
logger.info("Starting shared process...");
109-
} else if (event.state === SharedProcessState.Ready) {
110-
logger.info("Shared process is ready!");
104+
if (event.state === SharedProcessState.Ready) {
111105
app.wss.clients.forEach((c) => sendSharedProcessReady(c));
112106
}
113107
});

packages/server/src/vscode/sharedProcess.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import { forkModule } from "./bootstrapFork";
66
import { StdioIpcHandler } from "../ipc";
77
import { ParsedArgs } from "vs/platform/environment/common/environment";
88
import { LogLevel } from "vs/platform/log/common/log";
9-
import { Emitter, Event } from "@coder/events/src";
9+
import { Emitter } from "@coder/events/src";
10+
import { retry } from "@coder/ide/src/retry";
1011

1112
export enum SharedProcessState {
1213
Stopped,
@@ -28,12 +29,14 @@ export class SharedProcess {
2829
private ipcHandler: StdioIpcHandler | undefined;
2930
private readonly onStateEmitter = new Emitter<SharedProcessEvent>();
3031
public readonly onState = this.onStateEmitter.event;
32+
private readonly retryName = "Shared process";
3133

3234
public constructor(
3335
private readonly userDataDir: string,
3436
private readonly builtInExtensionsDir: string,
3537
) {
36-
this.restart();
38+
retry.register(this.retryName, () => this.restart());
39+
retry.run(this.retryName);
3740
}
3841

3942
public get state(): SharedProcessState {
@@ -73,7 +76,7 @@ export class SharedProcess {
7376
state: SharedProcessState.Stopped,
7477
});
7578
}
76-
this.restart();
79+
retry.run(this.retryName, new Error(`Exited with ${err}`));
7780
});
7881
this.ipcHandler = new StdioIpcHandler(this.activeProcess);
7982
this.ipcHandler.once("handshake:hello", () => {
@@ -94,6 +97,7 @@ export class SharedProcess {
9497
});
9598
this.ipcHandler.once("handshake:im ready", () => {
9699
resolved = true;
100+
retry.recover(this.retryName);
97101
this.setState({
98102
state: SharedProcessState.Ready,
99103
});

0 commit comments

Comments
 (0)