@@ -26,6 +26,7 @@ import java.util.*
26
26
import java.util.concurrent.CompletableFuture
27
27
import java.util.concurrent.ConcurrentHashMap
28
28
import java.util.concurrent.atomic.AtomicReference
29
+ import java.util.concurrent.Semaphore
29
30
30
31
@Suppress(" UnstableApiUsage" )
31
32
abstract class AbstractGitpodPortForwardingService : GitpodPortForwardingService {
@@ -50,6 +51,13 @@ abstract class AbstractGitpodPortForwardingService : GitpodPortForwardingService
50
51
// Debounce job for port updates
51
52
private var debounceJob: Job ? = null
52
53
54
+ // Batch size for port operations
55
+ private val BATCH_SIZE = 10
56
+ private val BATCH_DELAY = 100
57
+
58
+ // Semaphore to limit concurrent operations
59
+ private val operationSemaphore = Semaphore (10 )
60
+
53
61
init { start() }
54
62
55
63
private fun start () {
@@ -106,7 +114,7 @@ abstract class AbstractGitpodPortForwardingService : GitpodPortForwardingService
106
114
107
115
// Create new debounce job
108
116
debounceJob = runJob(lifetime) {
109
- delay(300 ) // 300ms debounce delay
117
+ delay(500 )
110
118
try {
111
119
if (syncInProgress.compareAndSet(false , true )) {
112
120
try {
@@ -175,30 +183,56 @@ abstract class AbstractGitpodPortForwardingService : GitpodPortForwardingService
175
183
.map { it.hostPortNumber }
176
184
.filter { portsNumbersFromNonServedPorts.contains(it) || ! portsNumbersFromPortsList.contains(it) }
177
185
178
- // Process port changes in background
186
+ // Process port changes in background with batching
179
187
runJob(lifetime) {
180
188
try {
181
- // Stop unnecessary ports first
182
- forwardedPortsToStopForwarding.forEach { stopForwarding(it) }
183
- exposedPortsToStopExposingOnClient.forEach { stopExposingOnClient(it) }
184
-
185
- // Start necessary ports
186
- servedPortsToStartForwarding.forEach {
187
- startForwarding(it)
188
- allPortsToKeep.add(it.localPort)
189
+ forwardedPortsToStopForwarding.chunked(BATCH_SIZE ).forEach { batch ->
190
+ batch.forEach { port ->
191
+ operationSemaphore.withPermit {
192
+ stopForwarding(port)
193
+ }
194
+ }
195
+ delay(100 ) // Add delay between batches
189
196
}
190
197
191
- exposedPortsToStartExposingOnClient.forEach {
192
- startExposingOnClient(it)
193
- allPortsToKeep.add(it.localPort)
198
+ exposedPortsToStopExposingOnClient.chunked(BATCH_SIZE ).forEach { batch ->
199
+ batch.forEach { port ->
200
+ operationSemaphore.withPermit {
201
+ stopExposingOnClient(port)
202
+ }
203
+ }
204
+ delay(BATCH_DELAY )
194
205
}
195
206
196
- // Update presentation for all ports
197
- application.invokeLater {
198
- portsList.forEach {
199
- updatePortsPresentation(it)
200
- allPortsToKeep.add(it.localPort)
207
+ servedPortsToStartForwarding.chunked(BATCH_SIZE ).forEach { batch ->
208
+ batch.forEach { port ->
209
+ operationSemaphore.withPermit {
210
+ startForwarding(port)
211
+ allPortsToKeep.add(port.localPort)
212
+ }
213
+ }
214
+ delay(BATCH_DELAY )
215
+ }
216
+
217
+ exposedPortsToStartExposingOnClient.chunked(BATCH_SIZE ).forEach { batch ->
218
+ batch.forEach { port ->
219
+ operationSemaphore.withPermit {
220
+ startExposingOnClient(port)
221
+ allPortsToKeep.add(port.localPort)
222
+ }
223
+ }
224
+ delay(BATCH_DELAY )
225
+ }
226
+
227
+ // Update presentation for all ports in batches
228
+ portsList.chunked(BATCH_SIZE ).forEach { batch ->
229
+ application.invokeLater {
230
+ batch.forEach {
231
+ updatePortsPresentation(it)
232
+ allPortsToKeep.add(it.localPort)
233
+ }
201
234
}
235
+ delay(BATCH_DELAY )
202
236
}
203
237
204
238
cleanupUnusedLifetimes(allPortsToKeep)
0 commit comments