Skip to content

Commit a85ce65

Browse files
committed
[ws-manager-bridge] Make non-governing bridges distribute updates locally
1 parent 886643e commit a85ce65

File tree

2 files changed

+80
-30
lines changed

2 files changed

+80
-30
lines changed

components/ws-manager-bridge/ee/src/bridge.ts

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { injectable } from "inversify";
99
import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing";
1010
import { WorkspaceStatus, WorkspaceType, WorkspacePhase } from "@gitpod/ws-manager/lib";
1111
import { WorkspaceInstance } from "@gitpod/gitpod-protocol";
12-
import { log } from "@gitpod/gitpod-protocol/lib/util/logging";
12+
import { log, LogContext } from "@gitpod/gitpod-protocol/lib/util/logging";
1313

1414
@injectable()
1515
export class WorkspaceManagerBridgeEE extends WorkspaceManagerBridge {
@@ -37,17 +37,16 @@ export class WorkspaceManagerBridgeEE extends WorkspaceManagerBridge {
3737
}
3838
}
3939

40-
protected async updatePrebuiltWorkspace(ctx: TraceContext, status: WorkspaceStatus.AsObject) {
40+
protected async writePrebuiltUpdateToDB(ctx: TraceContext, userId: string, status: WorkspaceStatus.AsObject) {
4141
if (status.spec && status.spec.type != WorkspaceType.PREBUILD) {
4242
return;
4343
}
4444

4545
const instanceId = status.id!;
4646
const workspaceId = status.metadata!.metaId!;
47-
const userId = status.metadata!.owner!;
4847
const logCtx = { instanceId, workspaceId, userId };
4948

50-
const span = TraceContext.startSpan("updatePrebuiltWorkspace", ctx);
49+
const span = TraceContext.startSpan("writePrebuiltUpdateToDB", ctx);
5150
try {
5251
const prebuild = await this.workspaceDB.trace({span}).findPrebuildByWorkspaceID(status.metadata!.metaId!);
5352
if (!prebuild) {
@@ -83,12 +82,35 @@ export class WorkspaceManagerBridgeEE extends WorkspaceManagerBridge {
8382
}
8483
await this.workspaceDB.trace({span}).storePrebuiltWorkspace(prebuild);
8584
}
85+
} catch (e) {
86+
TraceContext.setError({span}, e);
87+
throw e;
88+
} finally {
89+
span.finish();
90+
}
91+
}
8692

87-
{ // notify about prebuild updated
88-
const info = (await this.workspaceDB.trace({span}).findPrebuildInfos([prebuild.id]))[0];
89-
if (info) {
90-
this.messagebus.notifyOnPrebuildUpdate({ info, status: prebuild.state });
91-
}
93+
protected async notifyOnPrebuiltUpdate(ctx: TraceContext, userId: string, status: WorkspaceStatus.AsObject) {
94+
if (status.spec && status.spec.type != WorkspaceType.PREBUILD) {
95+
return;
96+
}
97+
const instanceId = status.id!;
98+
const workspaceId = status.metadata!.metaId!;
99+
const logCtx: LogContext = { instanceId, workspaceId, userId };
100+
101+
const span = TraceContext.startSpan("notifyOnPrebuiltUpdate", ctx);
102+
try {
103+
const prebuild = await this.workspaceDB.trace({span}).findPrebuildByWorkspaceID(status.metadata!.metaId!);
104+
if (!prebuild) {
105+
log.warn(logCtx, "headless workspace without prebuild");
106+
TraceContext.setError({span}, new Error("headless workspace without prebuild"));
107+
return
108+
}
109+
110+
// notify about prebuild updated
111+
const info = (await this.workspaceDB.trace({span}).findPrebuildInfos([prebuild.id]))[0];
112+
if (info) {
113+
this.messagebus.notifyOnPrebuildUpdate({ info, status: prebuild.state });
92114
}
93115
} catch (e) {
94116
TraceContext.setError({span}, e);

components/ws-manager-bridge/src/bridge.ts

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -59,22 +59,32 @@ export class WorkspaceManagerBridge implements Disposable {
5959
protected cluster: WorkspaceClusterInfo;
6060

6161
public start(cluster: WorkspaceClusterInfo, clientProvider: ClientProvider) {
62-
const logPayload = { name: cluster.name, url: cluster.url };
62+
const logPayload = { name: cluster.name, url: cluster.url, govern: cluster.govern };
6363
log.info(`starting bridge to cluster...`, logPayload);
6464
this.cluster = cluster;
6565

66-
if (cluster.govern) {
67-
log.debug(`starting DB updater: ${cluster.name}`, logPayload);
68-
/* no await */ this.startDatabaseUpdater(clientProvider, logPayload)
66+
const startStatusUpdateHandler = (writeToDB: boolean) => {
67+
log.debug(`starting status update handler: ${cluster.name}`, logPayload);
68+
/* no await */ this.startStatusUpdateHandler(clientProvider, writeToDB, logPayload)
6969
// this is a mere safe-guard: we do not expect the code inside to fail
70-
.catch(err => log.error("cannot start database updater", err));
70+
.catch(err => log.error("cannot start status update handler", err));
71+
};
7172

73+
if (cluster.govern) {
74+
// notify servers and _update the DB_
75+
startStatusUpdateHandler(true);
76+
77+
// the actual "governing" part
7278
const controllerInterval = this.config.controllerIntervalSeconds;
7379
if (controllerInterval <= 0) {
7480
throw new Error("controllerInterval <= 0!");
7581
}
7682
log.debug(`starting controller: ${cluster.name}`, logPayload);
7783
this.startController(clientProvider, controllerInterval, this.config.controllerMaxDisconnectSeconds);
84+
} else {
85+
// _DO NOT_ update the DB (another bridge is responsible for that)
86+
// Still, listen to all updates, generate/derive new state and distribute it locally!
87+
startStatusUpdateHandler(false);
7888
}
7989
log.info(`started bridge to cluster.`, logPayload);
8090
}
@@ -83,15 +93,15 @@ export class WorkspaceManagerBridge implements Disposable {
8393
this.dispose();
8494
}
8595

86-
protected async startDatabaseUpdater(clientProvider: ClientProvider, logPayload: {}): Promise<void> {
96+
protected async startStatusUpdateHandler(clientProvider: ClientProvider, writeToDB: boolean, logPayload: {}): Promise<void> {
8797
const subscriber = new WsmanSubscriber(clientProvider);
8898
this.disposables.push(subscriber);
8999

90100
const onReconnect = (ctx: TraceContext, s: WorkspaceStatus[]) => {
91-
s.forEach(sx => this.serializeMessagesByInstanceId<WorkspaceStatus>(ctx, sx, m => m.getId(), (ctx, msg) => this.handleStatusUpdate(ctx, msg)))
101+
s.forEach(sx => this.serializeMessagesByInstanceId<WorkspaceStatus>(ctx, sx, m => m.getId(), (ctx, msg) => this.handleStatusUpdate(ctx, msg, writeToDB)))
92102
};
93103
const onStatusUpdate = (ctx: TraceContext, s: WorkspaceStatus) => {
94-
this.serializeMessagesByInstanceId<WorkspaceStatus>(ctx, s, msg => msg.getId(), (ctx, s) => this.handleStatusUpdate(ctx, s))
104+
this.serializeMessagesByInstanceId<WorkspaceStatus>(ctx, s, msg => msg.getId(), (ctx, s) => this.handleStatusUpdate(ctx, s, writeToDB))
95105
};
96106
await subscriber.subscribe({ onReconnect, onStatusUpdate }, logPayload);
97107
}
@@ -110,7 +120,7 @@ export class WorkspaceManagerBridge implements Disposable {
110120
this.queues.set(instanceId, q);
111121
}
112122

113-
protected async handleStatusUpdate(ctx: TraceContext, rawStatus: WorkspaceStatus) {
123+
protected async handleStatusUpdate(ctx: TraceContext, rawStatus: WorkspaceStatus, writeToDB: boolean) {
114124
const status = rawStatus.toObject();
115125
if (!status.spec || !status.metadata || !status.conditions) {
116126
log.warn("Received invalid status update", status);
@@ -246,26 +256,40 @@ export class WorkspaceManagerBridge implements Disposable {
246256
break;
247257
}
248258

249-
await this.updatePrebuiltWorkspace({span}, status);
250-
251259
span.setTag("after", JSON.stringify(instance));
252-
await this.workspaceDB.trace({span}).storeInstance(instance);
253-
await this.messagebus.notifyOnInstanceUpdate({span}, userId, instance);
254260

255-
// important: call this after the DB update
256-
await this.cleanupProbeWorkspace({span}, status);
257-
258-
if (!!lifecycleHandler) {
259-
await lifecycleHandler();
261+
// now notify all listeners about updates - and update DB if needed
262+
if (writeToDB) {
263+
await this.writePrebuiltUpdateToDB({span}, userId, status);
264+
await this.writeStatusUpdateToDB({span}, userId, instance, status, lifecycleHandler);
260265
}
266+
await this.notifyOnPrebuiltUpdate({span}, userId, status);
267+
await this.notifyOnInstanceUpdate({span}, userId, instance);
268+
261269
} catch (e) {
262-
TraceContext.setError({ span }, e);
270+
TraceContext.setError({span}, e);
263271
throw e;
264272
} finally {
265273
span.finish();
266274
}
267275
}
268276

277+
protected async writeStatusUpdateToDB(ctx: TraceContext, userId: string, instance: WorkspaceInstance, status: WorkspaceStatus.AsObject, lifecycleHandler: (() => Promise<void>) | undefined): Promise<void> {
278+
await this.workspaceDB.trace(ctx).storeInstance(instance);
279+
280+
// cleanup
281+
// important: call this after the DB update
282+
await this.cleanupProbeWorkspace(ctx, status);
283+
284+
if (!!lifecycleHandler) {
285+
await lifecycleHandler();
286+
}
287+
}
288+
289+
protected async notifyOnInstanceUpdate(ctx: TraceContext, userId: string, instance: WorkspaceInstance) {
290+
await this.messagebus.notifyOnInstanceUpdate(ctx, userId, instance);
291+
}
292+
269293
protected startController(clientProvider: ClientProvider, controllerIntervalSeconds: number, controllerMaxDisconnectSeconds: number, maxTimeToRunningPhaseSeconds = 60 * 60) {
270294
let disconnectStarted = Number.MAX_SAFE_INTEGER;
271295
this.disposables.push(
@@ -321,7 +345,11 @@ export class WorkspaceManagerBridge implements Disposable {
321345
// probes are an EE feature - we just need the hook here
322346
}
323347

324-
protected async updatePrebuiltWorkspace(ctx: TraceContext, status: WorkspaceStatus.AsObject) {
348+
protected async writePrebuiltUpdateToDB(ctx: TraceContext, userId: string, status: WorkspaceStatus.AsObject) {
349+
// prebuilds are an EE feature - we just need the hook here
350+
}
351+
352+
protected async notifyOnPrebuiltUpdate(ctx: TraceContext, userId: string, status: WorkspaceStatus.AsObject) {
325353
// prebuilds are an EE feature - we just need the hook here
326354
}
327355

@@ -334,7 +362,7 @@ export class WorkspaceManagerBridge implements Disposable {
334362

335363
try {
336364
await this.userDB.trace({span}).deleteGitpodTokensNamedLike(ownerUserID, `${instance.id}-%`);
337-
await this.analytics.track({
365+
this.analytics.track({
338366
userId: ownerUserID,
339367
event: "workspace-stopped",
340368
messageId: `bridge-wsstopped-${instance.id}`,

0 commit comments

Comments
 (0)