Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7c401fe

Browse files
committed
Use web socket for workspace notifications
Previously, we were polling all the user's workspaces to get information about autostop and deletion so we can notify the user when those things are about to happen. This has a few problems: 1. We really only want to notify for the workspace to which we are actively connected, not all workspaces. 2. It did not account for owners being connected to someone else's workspace. We would have to query all workspaces, which is an even more expensive query. 3. If the sidebar is open, we are running two of the same query every five seconds, which is wasteful. We already had a web socket that was used to notify about the workspace being outdated, so I broke that into a new class and combined all the notification (outdated, autostop, deletion), status bar updates (just shows if the workspace is outdated), and context updates into one place using that web socket.
1 parent e72e697 commit 7c401fe

File tree

3 files changed

+170
-271
lines changed

3 files changed

+170
-271
lines changed

β€Žsrc/remote.ts

+7-94
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { isAxiosError } from "axios"
22
import { Api } from "coder/site/src/api/api"
33
import { Workspace } from "coder/site/src/api/typesGenerated"
4-
import EventSource from "eventsource"
54
import find from "find-process"
65
import * as fs from "fs/promises"
76
import * as jsonc from "jsonc-parser"
@@ -20,7 +19,7 @@ import { SSHConfig, SSHValues, mergeSSHConfigValues } from "./sshConfig"
2019
import { computeSSHProperties, sshSupportsSetEnv } from "./sshSupport"
2120
import { Storage } from "./storage"
2221
import { AuthorityPrefix, expandPath, parseRemoteAuthority } from "./util"
23-
import { WorkspaceAction } from "./workspaceAction"
22+
import { WorkspaceMonitor } from "./workspaceMonitor"
2423

2524
export interface RemoteDetails extends vscode.Disposable {
2625
url: string
@@ -292,9 +291,6 @@ export class Remote {
292291
// Register before connection so the label still displays!
293292
disposables.push(this.registerLabelFormatter(remoteAuthority, workspace.owner_name, workspace.name))
294293

295-
// Initialize any WorkspaceAction notifications (auto-off, upcoming deletion)
296-
const action = await WorkspaceAction.init(this.vscodeProposed, workspaceRestClient, this.storage)
297-
298294
// If the workspace is not in a running state, try to get it running.
299295
const updatedWorkspace = await this.maybeWaitForRunning(workspaceRestClient, workspace)
300296
if (!updatedWorkspace) {
@@ -376,88 +372,9 @@ export class Remote {
376372
}
377373
}
378374

379-
// Watch for workspace updates.
380-
this.storage.writeToCoderOutputChannel(`Establishing watcher for ${workspaceName}...`)
381-
const workspaceUpdate = new vscode.EventEmitter<Workspace>()
382-
const watchURL = new URL(`${baseUrlRaw}/api/v2/workspaces/${workspace.id}/watch`)
383-
const eventSource = new EventSource(watchURL.toString(), {
384-
headers: {
385-
"Coder-Session-Token": token,
386-
},
387-
})
388-
389-
const workspaceUpdatedStatus = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 999)
390-
disposables.push(workspaceUpdatedStatus)
391-
392-
let hasShownOutdatedNotification = false
393-
const refreshWorkspaceUpdatedStatus = (newWorkspace: Workspace) => {
394-
// If the newly gotten workspace was updated, then we show a notification
395-
// to the user that they should update. Only show this once per session.
396-
if (newWorkspace.outdated && !hasShownOutdatedNotification) {
397-
hasShownOutdatedNotification = true
398-
workspaceRestClient
399-
.getTemplate(newWorkspace.template_id)
400-
.then((template) => {
401-
return workspaceRestClient.getTemplateVersion(template.active_version_id)
402-
})
403-
.then((version) => {
404-
let infoMessage = `A new version of your workspace is available.`
405-
if (version.message) {
406-
infoMessage = `A new version of your workspace is available: ${version.message}`
407-
}
408-
vscode.window.showInformationMessage(infoMessage, "Update").then((action) => {
409-
if (action === "Update") {
410-
vscode.commands.executeCommand("coder.workspace.update", newWorkspace, workspaceRestClient)
411-
}
412-
})
413-
})
414-
}
415-
if (!newWorkspace.outdated) {
416-
vscode.commands.executeCommand("setContext", "coder.workspace.updatable", false)
417-
workspaceUpdatedStatus.hide()
418-
return
419-
}
420-
workspaceUpdatedStatus.name = "Coder Workspace Update"
421-
workspaceUpdatedStatus.text = "$(fold-up) Update Workspace"
422-
workspaceUpdatedStatus.command = "coder.workspace.update"
423-
// Important for hiding the "Update Workspace" command.
424-
vscode.commands.executeCommand("setContext", "coder.workspace.updatable", true)
425-
workspaceUpdatedStatus.show()
426-
}
427-
// Show an initial status!
428-
refreshWorkspaceUpdatedStatus(workspace)
429-
430-
eventSource.addEventListener("data", (event: MessageEvent<string>) => {
431-
const workspace = JSON.parse(event.data) as Workspace
432-
if (!workspace) {
433-
return
434-
}
435-
refreshWorkspaceUpdatedStatus(workspace)
436-
this.commands.workspace = workspace
437-
workspaceUpdate.fire(workspace)
438-
if (workspace.latest_build.status === "stopping" || workspace.latest_build.status === "stopped") {
439-
const action = this.vscodeProposed.window.showInformationMessage(
440-
"Your workspace stopped!",
441-
{
442-
useCustom: true,
443-
modal: true,
444-
detail: "Reloading the window will start it again.",
445-
},
446-
"Reload Window",
447-
)
448-
if (!action) {
449-
return
450-
}
451-
this.reloadWindow()
452-
}
453-
// If a new build is initialized for a workspace, we automatically
454-
// reload the window. Then the build log will appear, and startup
455-
// will continue as expected.
456-
if (workspace.latest_build.status === "starting") {
457-
this.reloadWindow()
458-
return
459-
}
460-
})
375+
// Watch the workspace for changes.
376+
const monitor = new WorkspaceMonitor(workspace, workspaceRestClient, this.storage)
377+
disposables.push(monitor)
461378

462379
// Wait for the agent to connect.
463380
if (agent.status === "connecting") {
@@ -469,7 +386,7 @@ export class Remote {
469386
},
470387
async () => {
471388
await new Promise<void>((resolve) => {
472-
const updateEvent = workspaceUpdate.event((workspace) => {
389+
const updateEvent = monitor.onChange.event((workspace) => {
473390
if (!agent) {
474391
return
475392
}
@@ -552,8 +469,6 @@ export class Remote {
552469
url: baseUrlRaw,
553470
token,
554471
dispose: () => {
555-
eventSource.close()
556-
action.cleanupWorkspaceActions()
557472
disposables.forEach((d) => d.dispose())
558473
},
559474
}
@@ -735,7 +650,7 @@ export class Remote {
735650
} else {
736651
statusText += network.preferred_derp + " "
737652
networkStatus.tooltip =
738-
"You're connected through a relay πŸ•΅οΈ.\nWe'll switch over to peer-to-peer when available."
653+
"You're connected through a relay πŸ•΅.\nWe'll switch over to peer-to-peer when available."
739654
}
740655
networkStatus.tooltip +=
741656
"\n\nDownload ↓ " +
@@ -751,9 +666,7 @@ export class Remote {
751666
if (!network.p2p) {
752667
const derpLatency = network.derp_latency[network.preferred_derp]
753668

754-
networkStatus.tooltip += `You ↔ ${derpLatency.toFixed(2)}ms ↔ ${network.preferred_derp} ↔ ${(
755-
network.latency - derpLatency
756-
).toFixed(2)}ms ↔ Workspace`
669+
networkStatus.tooltip += `You ↔ ${derpLatency.toFixed(2)}ms ↔ ${network.preferred_derp} ↔ ${(network.latency - derpLatency).toFixed(2)}ms ↔ Workspace`
757670

758671
let first = true
759672
Object.keys(network.derp_latency).forEach((region) => {

β€Žsrc/workspaceAction.ts

-177
This file was deleted.

0 commit comments

Comments
Β (0)