Skip to content

Commit 1577b73

Browse files
authored
fix: don't create new environment instances every time we poll (#239)
The polling logic has a list of "environments" which is updated by the poll loop. The loop retrieves a list of workspaces and agents from the backend, and then it looks if the pairs are already part of the existing environments. This is where we have an issue: we look by temporarily creating a new "RemoteEnvironment" and use the id of the temporary env. to search in the existing collection. Besides creating a lot of temporary objects every 5 seconds, the logic has another flaw: the constructor of the RemoteEnvironment triggers the ssh connection for that workspace if it was marked in the settings store as connected in the previous state. Asking to establish the ssh connection every 5 seconds is completely unnecessary, from my testings the newer versions of Toolbox are able to remember in the same session that a connection was opened, and it needs to be re-established again if it fails during that session. From now on the constructor for the RemoteEnvironment will request it only for new login sessions, from then on it is up to TBX to remember the connection state. In the early versions we added custom logic to retrigger the ssh connection when API keys expired. Turns out that is also no longer necessary to do. When an API key expires the user will have to go again through the login sequence, which means starting with a clean sheet of "environments". At the first poll run the RemoteEnvironment constructor will request TBX to establish the ssh connection again.
1 parent b32fb9b commit 1577b73

5 files changed

Lines changed: 17 additions & 56 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
- the proxy command now always logs in verbose mode when the log directory is enabled
88

9+
### Fixed
10+
11+
- no longer spam Toolbox with requests to establish the SSH connections every 5 seconds
12+
913
## 0.8.1 - 2025-12-11
1014

1115
### Changed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
version=0.8.1
1+
version=0.8.2
22
group=com.coder.toolbox
33
name=coder-toolbox

src/main/kotlin/com/coder/toolbox/CoderRemoteEnvironment.kt

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class CoderRemoteEnvironment(
7373

7474
init {
7575
if (context.settingsStore.shouldAutoConnect(id)) {
76-
context.logger.info("resuming SSH connection to $id — last session was still active.")
76+
context.logger.info("Last session to $id was still active, trying to establish SSH connection")
7777
startSshConnection()
7878
}
7979
refreshAvailableActions()
@@ -304,18 +304,15 @@ class CoderRemoteEnvironment(
304304
}
305305

306306
/**
307-
* Launches the SSH connection if the workspace is ready and there is no connection already established.
308-
*
309-
* Returns true if the SSH connection was scheduled to start, false otherwise.
307+
* Schedules the SSH connection to start as soon as possible if the workspace is ready and there is no connection already established.
310308
*/
311-
fun startSshConnection(): Boolean {
309+
fun startSshConnection() {
312310
if (environmentStatus.ready() && !isConnected.value) {
313311
connectionRequest.update {
314312
true
315313
}
316-
return true
314+
context.logger.info("Workspace status is ready and there is no existing connection, resuming SSH connection to $id")
317315
}
318-
return false
319316
}
320317

321318
override val deleteActionFlow: StateFlow<(() -> Unit)?> = MutableStateFlow(null)

src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -131,15 +131,14 @@ class CoderRemoteProvider(
131131
// different information?
132132
it.name
133133
}?.map { agent ->
134-
// If we have an environment already, update that.
135-
val env = CoderRemoteEnvironment(context, client, cli, ws, agent)
136-
lastEnvironments.firstOrNull { it == env }?.let {
137-
it.update(ws, agent)
138-
it
139-
} ?: env
134+
lastEnvironments.firstOrNull { it.id == "${ws.name}.${agent.name}" }
135+
?.also {
136+
// If we have an environment already, update that.
137+
it.update(ws, agent)
138+
} ?: CoderRemoteEnvironment(context, client, cli, ws, agent)
140139
} ?: emptyList()
141140
}
142-
}.toSet()
141+
}.toSet().sortedBy { it.id }
143142

144143
// In case we logged out while running the query.
145144
if (!isActive) {
@@ -153,7 +152,7 @@ class CoderRemoteProvider(
153152
}
154153

155154
environments.update {
156-
LoadableState.Value(resolvedEnvironments.toList())
155+
LoadableState.Value(resolvedEnvironments)
157156
}
158157
if (!isInitialized.value) {
159158
context.logger.info("Environments for ${client.url} are now initialized")
@@ -163,23 +162,8 @@ class CoderRemoteProvider(
163162
}
164163
lastEnvironments.apply {
165164
clear()
166-
addAll(resolvedEnvironments.sortedBy { it.id })
165+
addAll(resolvedEnvironments)
167166
}
168-
169-
if (WorkspaceConnectionManager.shouldEstablishWorkspaceConnections) {
170-
WorkspaceConnectionManager.allConnected().forEach { wsId ->
171-
val env = lastEnvironments.firstOrNull() { it.id == wsId }
172-
if (env != null && !env.isConnected()) {
173-
context.logger.info("Establishing lost SSH connection for workspace with id $wsId")
174-
if (!env.startSshConnection()) {
175-
context.logger.info("Can't establish lost SSH connection for workspace with id $wsId")
176-
}
177-
}
178-
}
179-
WorkspaceConnectionManager.reset()
180-
}
181-
182-
WorkspaceConnectionManager.collectStatuses(lastEnvironments)
183167
} catch (_: CancellationException) {
184168
context.logger.debug("${client.url} polling loop canceled")
185169
break
@@ -190,7 +174,6 @@ class CoderRemoteProvider(
190174
} else {
191175
context.logger.error(ex, "workspace polling error encountered")
192176
if (ex is APIResponseException && ex.isTokenExpired) {
193-
WorkspaceConnectionManager.shouldEstablishWorkspaceConnections = true
194177
close()
195178
context.envPageManager.showPluginEnvironmentsPage()
196179
errorBuffer.add(ex)
@@ -225,7 +208,6 @@ class CoderRemoteProvider(
225208
*/
226209
private fun logout() {
227210
context.logger.info("Logging out ${client?.me?.username}...")
228-
WorkspaceConnectionManager.reset()
229211
close()
230212
context.logger.info("User ${client?.me?.username} logged out successfully")
231213
}

src/main/kotlin/com/coder/toolbox/WorkspaceConnectionManager.kt

Lines changed: 0 additions & 22 deletions
This file was deleted.

0 commit comments

Comments
 (0)