Skip to content

Commit cf0534f

Browse files
committed
chore(runner): connect tunnel before connecting pb ws
1 parent f957fbb commit cf0534f

File tree

1 file changed

+59
-8
lines changed
  • sdks/typescript/runner/src

1 file changed

+59
-8
lines changed

sdks/typescript/runner/src/mod.ts

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -215,8 +215,16 @@ export class Runner {
215215
this.#started = true;
216216

217217
//console.log("[RUNNER] Starting runner");
218-
await this.#openPegboardWebSocket();
219-
this.#openTunnel();
218+
219+
try {
220+
// Connect tunnel first and wait for it to be ready before connecting runner WebSocket
221+
// This prevents a race condition where the runner appears ready but can't accept network requests
222+
await this.#openTunnelAndWait();
223+
await this.#openPegboardWebSocket();
224+
} catch (error) {
225+
this.#started = false;
226+
throw error;
227+
}
220228

221229
if (!this.#config.noAutoShutdown) {
222230
process.on("SIGTERM", this.shutdown.bind(this, false, true));
@@ -373,6 +381,44 @@ export class Runner {
373381
return `${wsEndpoint}/tunnel?namespace=${encodeURIComponent(this.#config.namespace)}`;
374382
}
375383

384+
async #openTunnelAndWait(): Promise<void> {
385+
return new Promise((resolve, reject) => {
386+
const url = this.pegboardRelayUrl;
387+
//console.log("[RUNNER] Opening tunnel to:", url);
388+
//console.log("[RUNNER] Current runner ID:", this.runnerId || "none");
389+
//console.log("[RUNNER] Active actors count:", this.#actors.size);
390+
391+
let connected = false;
392+
393+
this.#tunnel = new Tunnel(url);
394+
this.#tunnel.setCallbacks({
395+
fetch: this.#config.fetch,
396+
websocket: this.#config.websocket,
397+
onConnected: () => {
398+
if (!connected) {
399+
connected = true;
400+
//console.log("[RUNNER] Tunnel connected");
401+
resolve();
402+
}
403+
},
404+
onDisconnected: () => {
405+
if (!connected) {
406+
// First connection attempt failed
407+
reject(new Error("Tunnel connection failed"));
408+
}
409+
// If already connected, tunnel will handle reconnection automatically
410+
},
411+
});
412+
this.#tunnel.start();
413+
414+
// Re-register all active actors with the new tunnel
415+
for (const actorId of this.#actors.keys()) {
416+
//console.log("[RUNNER] Re-registering actor with tunnel:", actorId);
417+
this.#tunnel.registerActor(actorId);
418+
}
419+
});
420+
}
421+
376422
#openTunnel() {
377423
const url = this.pegboardRelayUrl;
378424
//console.log("[RUNNER] Opening tunnel to:", url);
@@ -500,6 +546,7 @@ export class Runner {
500546
// Handle message
501547
if (message.tag === "ToClientInit") {
502548
const init = message.val;
549+
const hadRunnerId = !!this.runnerId;
503550
this.runnerId = init.runnerId;
504551

505552
// Store the runner lost threshold from metadata
@@ -513,13 +560,17 @@ export class Runner {
513560
// runnerLostThreshold: this.#runnerLostThreshold,
514561
//});
515562

516-
// Reopen tunnel with runner ID
517-
//console.log("[RUNNER] Received runner ID, reopening tunnel");
518-
if (this.#tunnel) {
519-
//console.log("[RUNNER] Shutting down existing tunnel");
520-
this.#tunnel.shutdown();
563+
// Only reopen tunnel if we didn't have a runner ID before
564+
// This happens on reconnection after losing connection
565+
if (!hadRunnerId && this.runnerId) {
566+
// Reopen tunnel with runner ID
567+
//console.log("[RUNNER] Received runner ID, reopening tunnel");
568+
if (this.#tunnel) {
569+
//console.log("[RUNNER] Shutting down existing tunnel");
570+
this.#tunnel.shutdown();
571+
}
572+
this.#openTunnel();
521573
}
522-
this.#openTunnel();
523574

524575
// Resend events that haven't been acknowledged
525576
this.#resendUnacknowledgedEvents(init.lastEventIdx);

0 commit comments

Comments
 (0)