Skip to content

[ws-man-bridge] Remove non-governing codepaths #16637

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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