Skip to content

Commit

Permalink
[ws-man-bridge] Remove non-governing codepaths (gitpod-io#16637)
Browse files Browse the repository at this point in the history
* [ws-man-bridge] Remove non-governing codepaths

* Fix
  • Loading branch information
easyCZ authored Mar 6, 2023
1 parent e51ff3e commit 79506aa
Show file tree
Hide file tree
Showing 7 changed files with 44 additions and 194 deletions.
15 changes: 3 additions & 12 deletions components/ws-manager-bridge/ee/src/prebuild-updater-db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,7 @@ export class PrebuildUpdaterDB implements PrebuildUpdater {
@inject(PrometheusMetricsExporter)
protected readonly prometheusExporter: PrometheusMetricsExporter;

async updatePrebuiltWorkspace(
ctx: TraceContext,
userId: string,
status: WorkspaceStatus.AsObject,
writeToDB: boolean,
) {
async updatePrebuiltWorkspace(ctx: TraceContext, userId: string, status: WorkspaceStatus.AsObject) {
if (status.spec && status.spec.type != WorkspaceType.PREBUILD) {
return;
}
Expand Down Expand Up @@ -81,20 +76,16 @@ export class PrebuildUpdaterDB implements PrebuildUpdater {
span.setTag("updatePrebuildWorkspace.prebuild.error", updatedPrebuild.error);

// Here we make sure that we increment the counter only when:
// 1. the instance is governing ("writeToDB"), so that we don't get metrics from multiple pods,
// 2. the state changes (we can receive multiple events with the same state)
// the state changes (we can receive multiple events with the same state)
if (
writeToDB &&
updatedPrebuild.state &&
terminatingStates.includes(updatedPrebuild.state) &&
updatedPrebuild.state !== prebuild.state
) {
this.prometheusExporter.increasePrebuildsCompletedCounter(updatedPrebuild.state);
}

if (writeToDB) {
await this.workspaceDB.trace({ span }).storePrebuiltWorkspace(updatedPrebuild);
}
await this.workspaceDB.trace({ span }).storePrebuiltWorkspace(updatedPrebuild);

// notify updates
// headless update
Expand Down
95 changes: 34 additions & 61 deletions components/ws-manager-bridge/src/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* See License.AGPL.txt in the project root for license information.
*/

import { inject, injectable, interfaces } from "inversify";
import { inject, injectable } from "inversify";
import { MessageBusIntegration } from "./messagebus-integration";
import {
Disposable,
Expand All @@ -29,7 +29,6 @@ import { ClientProvider, WsmanSubscriber } from "./wsman-subscriber";
import { Timestamp } from "google-protobuf/google/protobuf/timestamp_pb";
import { Configuration } from "./config";
import { WorkspaceCluster } from "@gitpod/gitpod-protocol/lib/workspace-cluster";
import { PreparingUpdateEmulator, PreparingUpdateEmulatorFactory } from "./preparing-update-emulator";
import { performance } from "perf_hooks";
import { PrebuildUpdater } from "./prebuild-updater";
import { WorkspaceInstanceController } from "./workspace-instance-controller";
Expand All @@ -44,7 +43,7 @@ function toBool(b: WorkspaceConditionBool | undefined): boolean | undefined {
return b === WorkspaceConditionBool.TRUE;
}

export type WorkspaceClusterInfo = Pick<WorkspaceCluster, "name" | "url" | "govern">;
export type WorkspaceClusterInfo = Pick<WorkspaceCluster, "name" | "url">;

@injectable()
export class WorkspaceManagerBridge implements Disposable {
Expand All @@ -60,9 +59,6 @@ export class WorkspaceManagerBridge implements Disposable {
@inject(Configuration)
protected readonly config: Configuration;

@inject(PreparingUpdateEmulatorFactory)
protected readonly preparingUpdateEmulatorFactory: interfaces.Factory<PreparingUpdateEmulator>;

@inject(IAnalyticsWriter)
protected readonly analytics: IAnalyticsWriter;

Expand All @@ -78,57 +74,43 @@ export class WorkspaceManagerBridge implements Disposable {
protected cluster: WorkspaceClusterInfo;

public start(cluster: WorkspaceClusterInfo, clientProvider: ClientProvider) {
const logPayload = { name: cluster.name, url: cluster.url, govern: cluster.govern };
const logPayload = { name: cluster.name, url: cluster.url };
log.info(`Starting bridge to cluster...`, logPayload);
this.cluster = cluster;

const startStatusUpdateHandler = (writeToDB: boolean) => {
const startStatusUpdateHandler = () => {
log.debug(`Starting status update handler: ${cluster.name}`, logPayload);
/* no await */ this.startStatusUpdateHandler(clientProvider, writeToDB, logPayload)
/* no await */ this.startStatusUpdateHandler(clientProvider, logPayload)
// this is a mere safe-guard: we do not expect the code inside to fail
.catch((err) => log.error("Cannot start status update handler", err));
};

if (cluster.govern) {
// notify servers and _update the DB_
startStatusUpdateHandler(true);

// the actual "governing" part
const controllerIntervalSeconds = this.config.controllerIntervalSeconds;
if (controllerIntervalSeconds <= 0) {
throw new Error("controllerIntervalSeconds <= 0!");
}
// notify servers and _update the DB_
startStatusUpdateHandler();

log.debug(`Starting controller: ${cluster.name}`, logPayload);
// Control all workspace instances, either against ws-manager or configured timeouts
this.workspaceInstanceController.start(
cluster.name,
clientProvider,
controllerIntervalSeconds,
this.config.controllerMaxDisconnectSeconds,
);
} else {
// _DO NOT_ update the DB (another bridge is responsible for that)
// Still, listen to all updates, generate/derive new state and distribute it locally!
startStatusUpdateHandler(false);

// emulate WorkspaceInstance updates for all Workspaces in the "preparing" or "building" phase in this cluster
const updateEmulator = this.preparingUpdateEmulatorFactory() as PreparingUpdateEmulator;
this.disposables.push(updateEmulator);
updateEmulator.start(cluster.name);
// the actual "governing" part
const controllerIntervalSeconds = this.config.controllerIntervalSeconds;
if (controllerIntervalSeconds <= 0) {
throw new Error("controllerIntervalSeconds <= 0!");
}

log.debug(`Starting controller: ${cluster.name}`, logPayload);
// Control all workspace instances, either against ws-manager or configured timeouts
this.workspaceInstanceController.start(
cluster.name,
clientProvider,
controllerIntervalSeconds,
this.config.controllerMaxDisconnectSeconds,
);

log.info(`Started bridge to cluster.`, logPayload);
}

public stop() {
this.dispose();
}

protected async startStatusUpdateHandler(
clientProvider: ClientProvider,
writeToDB: boolean,
logPayload: {},
): Promise<void> {
protected async startStatusUpdateHandler(clientProvider: ClientProvider, logPayload: {}): Promise<void> {
const subscriber = new WsmanSubscriber(clientProvider);
this.disposables.push(subscriber);

Expand All @@ -138,7 +120,7 @@ export class WorkspaceManagerBridge implements Disposable {
ctx,
sx,
(m) => m.getId(),
(ctx, msg) => this.handleStatusUpdate(ctx, msg, writeToDB),
(ctx, msg) => this.handleStatusUpdate(ctx, msg),
),
);
};
Expand All @@ -147,7 +129,7 @@ export class WorkspaceManagerBridge implements Disposable {
ctx,
s,
(msg) => msg.getId(),
(ctx, s) => this.handleStatusUpdate(ctx, s, writeToDB),
(ctx, s) => this.handleStatusUpdate(ctx, s),
);
};
await subscriber.subscribe({ onReconnect, onStatusUpdate }, logPayload);
Expand All @@ -172,7 +154,7 @@ export class WorkspaceManagerBridge implements Disposable {
this.queues.set(instanceId, q);
}

protected async handleStatusUpdate(ctx: TraceContext, rawStatus: WorkspaceStatus, writeToDB: boolean) {
protected async handleStatusUpdate(ctx: TraceContext, rawStatus: WorkspaceStatus) {
const start = performance.now();
const status = rawStatus.toObject();
log.info("Handling WorkspaceStatus update", filterStatus(status));
Expand All @@ -189,17 +171,12 @@ export class WorkspaceManagerBridge implements Disposable {
};

try {
this.prometheusExporter.reportWorkspaceInstanceUpdateStarted(
writeToDB,
this.cluster.name,
status.spec.type,
);
await this.statusUpdate(ctx, rawStatus, writeToDB);
this.prometheusExporter.reportWorkspaceInstanceUpdateStarted(this.cluster.name, status.spec.type);
await this.statusUpdate(ctx, rawStatus);
} catch (e) {
const durationMs = performance.now() - start;
this.prometheusExporter.reportWorkspaceInstanceUpdateCompleted(
durationMs / 1000,
writeToDB,
this.cluster.name,
status.spec.type,
e,
Expand All @@ -210,14 +187,13 @@ export class WorkspaceManagerBridge implements Disposable {
const durationMs = performance.now() - start;
this.prometheusExporter.reportWorkspaceInstanceUpdateCompleted(
durationMs / 1000,
writeToDB,
this.cluster.name,
status.spec.type,
);
log.info(logCtx, "Successfully completed WorkspaceInstance status update");
}

private async statusUpdate(ctx: TraceContext, rawStatus: WorkspaceStatus, writeToDB: boolean) {
private async statusUpdate(ctx: TraceContext, rawStatus: WorkspaceStatus) {
const status = rawStatus.toObject();

if (!status.spec || !status.metadata || !status.conditions) {
Expand All @@ -226,7 +202,6 @@ export class WorkspaceManagerBridge implements Disposable {

const span = TraceContext.startSpan("handleStatusUpdate", ctx);
span.setTag("status", JSON.stringify(filterStatus(status)));
span.setTag("writeToDB", writeToDB);
span.setTag("statusVersion", status.statusVersion);
try {
// Beware of the ID mapping here: What's a workspace to the ws-manager is a workspace instance to the rest of the system.
Expand Down Expand Up @@ -388,16 +363,14 @@ export class WorkspaceManagerBridge implements Disposable {
span.setTag("after", JSON.stringify(instance));

// now notify all prebuild listeners about updates - and update DB if needed
await this.prebuildUpdater.updatePrebuiltWorkspace({ span }, userId, status, writeToDB);
await this.prebuildUpdater.updatePrebuiltWorkspace({ span }, userId, status);

if (writeToDB) {
await this.workspaceDB.trace(ctx).storeInstance(instance);
await this.workspaceDB.trace(ctx).storeInstance(instance);

// cleanup
// important: call this after the DB update
if (!!lifecycleHandler) {
await lifecycleHandler();
}
// cleanup
// important: call this after the DB update
if (!!lifecycleHandler) {
await lifecycleHandler();
}
await this.messagebus.notifyOnInstanceUpdate(ctx, userId, instance);
} catch (e) {
Expand Down
3 changes: 0 additions & 3 deletions components/ws-manager-bridge/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@ export interface Configuration {
stoppingPhaseSeconds: number;
};

// emulatePreparingIntervalSeconds configures how often we check for Workspaces in phase "preparing" for clusters we do not govern
emulatePreparingIntervalSeconds: number;

// clusterSyncIntervalSeconds configures how often we sync workspace cluster information
clusterSyncIntervalSeconds: number;
}
4 changes: 0 additions & 4 deletions components/ws-manager-bridge/src/container-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import { IAnalyticsWriter } from "@gitpod/gitpod-protocol/lib/analytics";
import { newAnalyticsWriterFromEnv } from "@gitpod/gitpod-protocol/lib/util/analytics";
import { IClientCallMetrics } from "@gitpod/gitpod-protocol/lib/util/grpc";
import { PrometheusClientCallMetrics } from "@gitpod/gitpod-protocol/lib/messaging/client-call-metrics";
import { PreparingUpdateEmulator, PreparingUpdateEmulatorFactory } from "./preparing-update-emulator";
import { PrebuildStateMapper } from "./prebuild-state-mapper";
import { PrebuildUpdater, PrebuildUpdaterNoOp } from "./prebuild-updater";
import { DebugApp } from "@gitpod/gitpod-protocol/lib/util/debug-app";
Expand Down Expand Up @@ -84,9 +83,6 @@ export const containerModule = new ContainerModule((bind) => {

bind(IAnalyticsWriter).toDynamicValue(newAnalyticsWriterFromEnv).inSingletonScope();

bind(PreparingUpdateEmulator).toSelf().inRequestScope();
bind(PreparingUpdateEmulatorFactory).toAutoFactory(PreparingUpdateEmulator);

bind(PrebuildStateMapper).toSelf().inSingletonScope();
bind(PrebuildUpdater).to(PrebuildUpdaterNoOp).inSingletonScope();

Expand Down
14 changes: 2 additions & 12 deletions components/ws-manager-bridge/src/prebuild-updater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,14 @@ import { injectable } from "inversify";
export const PrebuildUpdater = Symbol("PrebuildUpdater");

export interface PrebuildUpdater {
updatePrebuiltWorkspace(
ctx: TraceContext,
userId: string,
status: WorkspaceStatus.AsObject,
writeToDB: boolean,
): Promise<void>;
updatePrebuiltWorkspace(ctx: TraceContext, userId: string, status: WorkspaceStatus.AsObject): Promise<void>;

stopPrebuildInstance(ctx: TraceContext, instance: WorkspaceInstance): Promise<void>;
}

@injectable()
export class PrebuildUpdaterNoOp implements PrebuildUpdater {
async updatePrebuiltWorkspace(
ctx: TraceContext,
userId: string,
status: WorkspaceStatus.AsObject,
writeToDB: boolean,
): Promise<void> {}
async updatePrebuiltWorkspace(ctx: TraceContext, userId: string, status: WorkspaceStatus.AsObject): Promise<void> {}

async stopPrebuildInstance(ctx: TraceContext, instance: WorkspaceInstance): Promise<void> {}
}
96 changes: 0 additions & 96 deletions components/ws-manager-bridge/src/preparing-update-emulator.ts

This file was deleted.

Loading

0 comments on commit 79506aa

Please sign in to comment.