Skip to content

Commit 08ae6f8

Browse files
authored
Prevent parallel refreshes (#202)
1 parent 5054d75 commit 08ae6f8

File tree

1 file changed

+56
-18
lines changed

1 file changed

+56
-18
lines changed

src/workspacesProvider.ts

+56-18
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export enum WorkspaceQuery {
1414
export class WorkspaceProvider implements vscode.TreeDataProvider<vscode.TreeItem> {
1515
private workspaces: WorkspaceTreeItem[] = []
1616
private agentWatchers: Record<WorkspaceAgent["id"], { dispose: () => void; metadata?: AgentMetadataEvent[] }> = {}
17+
private fetching = false
1718

1819
constructor(
1920
private readonly getWorkspacesQuery: WorkspaceQuery,
@@ -22,26 +23,63 @@ export class WorkspaceProvider implements vscode.TreeDataProvider<vscode.TreeIte
2223
this.fetchAndRefresh()
2324
}
2425

25-
// fetchAndRefrehsh fetches new workspaces then re-renders the entire tree.
26+
// fetchAndRefresh fetches new workspaces then re-renders the entire tree.
27+
// Trying to call this while already refreshing is a no-op and will return
28+
// immediately.
2629
async fetchAndRefresh() {
27-
const token = await this.storage.getSessionToken()
28-
const workspacesTreeItem: WorkspaceTreeItem[] = []
30+
if (this.fetching) {
31+
return
32+
}
33+
this.fetching = true
34+
2935
Object.values(this.agentWatchers).forEach((watcher) => watcher.dispose())
30-
// If the URL is set then we are logged in.
31-
if (this.storage.getURL()) {
32-
const resp = await getWorkspaces({ q: this.getWorkspacesQuery })
33-
resp.workspaces.forEach((workspace) => {
34-
const showMetadata = this.getWorkspacesQuery === WorkspaceQuery.Mine
35-
if (showMetadata && token) {
36-
const agents = extractAgents(workspace)
37-
agents.forEach((agent) => this.monitorMetadata(agent.id, token)) // monitor metadata for all agents
38-
}
39-
const treeItem = new WorkspaceTreeItem(workspace, this.getWorkspacesQuery === WorkspaceQuery.All, showMetadata)
40-
workspacesTreeItem.push(treeItem)
41-
})
36+
37+
try {
38+
this.workspaces = await this.fetch()
39+
} catch (error) {
40+
this.workspaces = []
4241
}
43-
this.workspaces = workspacesTreeItem
42+
4443
this.refresh()
44+
this.fetching = false
45+
}
46+
47+
/**
48+
* Fetch workspaces and turn them into tree items. Throw an error if not
49+
* logged in or the query fails.
50+
*/
51+
async fetch(): Promise<WorkspaceTreeItem[]> {
52+
// Assume that no URL or no token means we are not logged in.
53+
const url = this.storage.getURL()
54+
const token = await this.storage.getSessionToken()
55+
if (!url || !token) {
56+
throw new Error("not logged in")
57+
}
58+
59+
const resp = await getWorkspaces({ q: this.getWorkspacesQuery })
60+
61+
// We could have logged out while waiting for the query, or logged into a
62+
// different deployment.
63+
const url2 = this.storage.getURL()
64+
const token2 = await this.storage.getSessionToken()
65+
if (!url2 || !token2) {
66+
throw new Error("not logged in")
67+
} else if (url !== url2) {
68+
// In this case we need to fetch from the new deployment instead.
69+
// TODO: It would be better to cancel this fetch when that happens,
70+
// because this means we have to wait for the old fetch to finish before
71+
// finally getting workspaces for the new one.
72+
return this.fetch()
73+
}
74+
75+
return resp.workspaces.map((workspace) => {
76+
const showMetadata = this.getWorkspacesQuery === WorkspaceQuery.Mine
77+
if (showMetadata) {
78+
const agents = extractAgents(workspace)
79+
agents.forEach((agent) => this.monitorMetadata(agent.id, url, token2)) // monitor metadata for all agents
80+
}
81+
return new WorkspaceTreeItem(workspace, this.getWorkspacesQuery === WorkspaceQuery.All, showMetadata)
82+
})
4583
}
4684

4785
private _onDidChangeTreeData: vscode.EventEmitter<vscode.TreeItem | undefined | null | void> =
@@ -78,8 +116,8 @@ export class WorkspaceProvider implements vscode.TreeDataProvider<vscode.TreeIte
78116

79117
// monitorMetadata opens an SSE endpoint to monitor metadata on the specified
80118
// agent and registers a disposer that can be used to stop the watch.
81-
monitorMetadata(agentId: WorkspaceAgent["id"], token: string): void {
82-
const agentMetadataURL = new URL(`${this.storage.getURL()}/api/v2/workspaceagents/${agentId}/watch-metadata`)
119+
monitorMetadata(agentId: WorkspaceAgent["id"], url: string, token: string): void {
120+
const agentMetadataURL = new URL(`${url}/api/v2/workspaceagents/${agentId}/watch-metadata`)
83121
const agentMetadataEventSource = new EventSource(agentMetadataURL.toString(), {
84122
headers: {
85123
"Coder-Session-Token": token,

0 commit comments

Comments
 (0)