Skip to content

Commit

Permalink
Remove extraneous polling (#382)
Browse files Browse the repository at this point in the history

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:

    We really only want to notify for the workspace to which we are
    actively connected, not all workspaces.
    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.
    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
notifications (outdated, autostop, deletion), status bar updates (just
shows if the workspace is outdated), and context updates into one place
using that web socket.

Secondly, fix an issue where we immediately start polling when connecting
to a remote even though the Coder sidebar is not visible.

Now, we should only be polling when the sidebar is visible.
  • Loading branch information
code-asher authored Oct 14, 2024
1 parent 62a3520 commit 39bbc89
Show file tree
Hide file tree
Showing 6 changed files with 226 additions and 276 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

## Unreleased

### Fixed

- The plugin no longer immediately starts polling workspaces when connecting to
a remote. It will only do this when the Coder sidebar is open.

### Changed

- Instead of monitoring all workspaces for impending autostops and deletions,
the plugin now only monitors the connected workspace.

## [v1.3.2](https://github.com/coder/vscode-coder/releases/tag/v1.3.2) (2024-09-10)

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ export class Commands {
{
useCustom: true,
modal: true,
detail: `${this.workspace.owner_name}/${this.workspace.name} will be updated then this window will reload to watch the build logs and reconnect.`,
detail: `Update ${this.workspace.owner_name}/${this.workspace.name} to the latest version?`,
},
"Update",
)
Expand Down
102 changes: 8 additions & 94 deletions src/remote.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { isAxiosError } from "axios"
import { Api } from "coder/site/src/api/api"
import { Workspace } from "coder/site/src/api/typesGenerated"
import EventSource from "eventsource"
import find from "find-process"
import * as fs from "fs/promises"
import * as jsonc from "jsonc-parser"
Expand All @@ -20,7 +19,7 @@ import { SSHConfig, SSHValues, mergeSSHConfigValues } from "./sshConfig"
import { computeSSHProperties, sshSupportsSetEnv } from "./sshSupport"
import { Storage } from "./storage"
import { AuthorityPrefix, expandPath, parseRemoteAuthority } from "./util"
import { WorkspaceAction } from "./workspaceAction"
import { WorkspaceMonitor } from "./workspaceMonitor"

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

// Initialize any WorkspaceAction notifications (auto-off, upcoming deletion)
const action = await WorkspaceAction.init(this.vscodeProposed, workspaceRestClient, this.storage)

// If the workspace is not in a running state, try to get it running.
const updatedWorkspace = await this.maybeWaitForRunning(workspaceRestClient, workspace)
if (!updatedWorkspace) {
Expand Down Expand Up @@ -376,88 +372,10 @@ export class Remote {
}
}

// Watch for workspace updates.
this.storage.writeToCoderOutputChannel(`Establishing watcher for ${workspaceName}...`)
const workspaceUpdate = new vscode.EventEmitter<Workspace>()
const watchURL = new URL(`${baseUrlRaw}/api/v2/workspaces/${workspace.id}/watch`)
const eventSource = new EventSource(watchURL.toString(), {
headers: {
"Coder-Session-Token": token,
},
})

const workspaceUpdatedStatus = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 999)
disposables.push(workspaceUpdatedStatus)

let hasShownOutdatedNotification = false
const refreshWorkspaceUpdatedStatus = (newWorkspace: Workspace) => {
// If the newly gotten workspace was updated, then we show a notification
// to the user that they should update. Only show this once per session.
if (newWorkspace.outdated && !hasShownOutdatedNotification) {
hasShownOutdatedNotification = true
workspaceRestClient
.getTemplate(newWorkspace.template_id)
.then((template) => {
return workspaceRestClient.getTemplateVersion(template.active_version_id)
})
.then((version) => {
let infoMessage = `A new version of your workspace is available.`
if (version.message) {
infoMessage = `A new version of your workspace is available: ${version.message}`
}
vscode.window.showInformationMessage(infoMessage, "Update").then((action) => {
if (action === "Update") {
vscode.commands.executeCommand("coder.workspace.update", newWorkspace, workspaceRestClient)
}
})
})
}
if (!newWorkspace.outdated) {
vscode.commands.executeCommand("setContext", "coder.workspace.updatable", false)
workspaceUpdatedStatus.hide()
return
}
workspaceUpdatedStatus.name = "Coder Workspace Update"
workspaceUpdatedStatus.text = "$(fold-up) Update Workspace"
workspaceUpdatedStatus.command = "coder.workspace.update"
// Important for hiding the "Update Workspace" command.
vscode.commands.executeCommand("setContext", "coder.workspace.updatable", true)
workspaceUpdatedStatus.show()
}
// Show an initial status!
refreshWorkspaceUpdatedStatus(workspace)

eventSource.addEventListener("data", (event: MessageEvent<string>) => {
const workspace = JSON.parse(event.data) as Workspace
if (!workspace) {
return
}
refreshWorkspaceUpdatedStatus(workspace)
this.commands.workspace = workspace
workspaceUpdate.fire(workspace)
if (workspace.latest_build.status === "stopping" || workspace.latest_build.status === "stopped") {
const action = this.vscodeProposed.window.showInformationMessage(
"Your workspace stopped!",
{
useCustom: true,
modal: true,
detail: "Reloading the window will start it again.",
},
"Reload Window",
)
if (!action) {
return
}
this.reloadWindow()
}
// If a new build is initialized for a workspace, we automatically
// reload the window. Then the build log will appear, and startup
// will continue as expected.
if (workspace.latest_build.status === "starting") {
this.reloadWindow()
return
}
})
// Watch the workspace for changes.
const monitor = new WorkspaceMonitor(workspace, workspaceRestClient, this.storage, this.vscodeProposed)
disposables.push(monitor)
disposables.push(monitor.onChange.event((w) => (this.commands.workspace = w)))

// Wait for the agent to connect.
if (agent.status === "connecting") {
Expand All @@ -469,7 +387,7 @@ export class Remote {
},
async () => {
await new Promise<void>((resolve) => {
const updateEvent = workspaceUpdate.event((workspace) => {
const updateEvent = monitor.onChange.event((workspace) => {
if (!agent) {
return
}
Expand Down Expand Up @@ -552,8 +470,6 @@ export class Remote {
url: baseUrlRaw,
token,
dispose: () => {
eventSource.close()
action.cleanupWorkspaceActions()
disposables.forEach((d) => d.dispose())
},
}
Expand Down Expand Up @@ -735,7 +651,7 @@ export class Remote {
} else {
statusText += network.preferred_derp + " "
networkStatus.tooltip =
"You're connected through a relay 🕵.\nWe'll switch over to peer-to-peer when available."
"You're connected through a relay 🕵.\nWe'll switch over to peer-to-peer when available."
}
networkStatus.tooltip +=
"\n\nDownload ↓ " +
Expand All @@ -751,9 +667,7 @@ export class Remote {
if (!network.p2p) {
const derpLatency = network.derp_latency[network.preferred_derp]

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

let first = true
Object.keys(network.derp_latency).forEach((region) => {
Expand Down
177 changes: 0 additions & 177 deletions src/workspaceAction.ts

This file was deleted.

Loading

0 comments on commit 39bbc89

Please sign in to comment.