Skip to content

Commit 6809f07

Browse files
committed
fix: shadow mode uses fire-and-forget HTTP to avoid holding connections
1 parent 64b08b9 commit 6809f07

File tree

4 files changed

+52
-23
lines changed

4 files changed

+52
-23
lines changed

apps/webapp/app/v3/services/computeTemplateCreation.server.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,20 @@ export class ComputeTemplateCreationService {
8383
return { success: false, error: message };
8484
}
8585
}
86+
87+
/**
88+
* Fire-and-forget template creation. No HTTP connection held open,
89+
* no response awaited. Used for shadow rollout mode.
90+
*/
91+
createTemplateBackground(imageReference: string): void {
92+
if (!this.client) {
93+
return;
94+
}
95+
96+
this.client.createTemplateBackground({
97+
image: imageReference,
98+
cpu: 0.5,
99+
memory_mb: 512,
100+
});
101+
}
86102
}

apps/webapp/app/v3/services/finalizeDeployment.server.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,13 +191,11 @@ export class FinalizeDeploymentService extends BaseService {
191191

192192
// Shadow mode: fire-and-forget template creation after deploy is finalized
193193
if (templateMode === "shadow" && deployment.imageReference) {
194-
templateService.createTemplate(deployment.imageReference).catch((error) => {
195-
logger.error("Shadow compute template creation failed", {
196-
id,
197-
imageReference: deployment.imageReference,
198-
error: error instanceof Error ? error.message : String(error),
199-
});
194+
logger.debug("Shadow compute template creation (background)", {
195+
id,
196+
imageReference: deployment.imageReference,
200197
});
198+
templateService.createTemplateBackground(deployment.imageReference);
201199
}
202200

203201
return finalizedDeployment;

apps/webapp/app/v3/services/finalizeDeploymentV2.server.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -241,13 +241,11 @@ export class FinalizeDeploymentV2Service extends BaseService {
241241
imageReference: string,
242242
deploymentFriendlyId: string
243243
) {
244-
templateService.createTemplate(imageReference).catch((error) => {
245-
logger.error("Shadow compute template creation failed", {
246-
id: deploymentFriendlyId,
247-
imageReference,
248-
error: error instanceof Error ? error.message : String(error),
249-
});
244+
logger.debug("Shadow compute template creation (background)", {
245+
id: deploymentFriendlyId,
246+
imageReference,
250247
});
248+
templateService.createTemplateBackground(imageReference);
251249
}
252250
}
253251

internal-packages/compute/src/gatewayClient.ts

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,32 @@ export class ComputeGatewayClient {
1313
req: TemplateCreateRequest,
1414
options?: { signal?: AbortSignal }
1515
): Promise<{ accepted: boolean }> {
16+
const response = await this.#fetch(req, options?.signal);
17+
18+
if (!response.ok) {
19+
const errorBody = await response.text().catch(() => "unknown error");
20+
throw new Error(`Gateway template creation failed (${response.status}): ${errorBody}`);
21+
}
22+
23+
return { accepted: response.status === 202 };
24+
}
25+
26+
/**
27+
* Fire-and-forget template creation. Sends the request but does not
28+
* await the response, so no HTTP connection is held open.
29+
*/
30+
createTemplateBackground(req: TemplateCreateRequest): void {
31+
this.#fetch(req).then(
32+
(response) => {
33+
if (!response.ok) {
34+
response.text().catch(() => {});
35+
}
36+
},
37+
() => {} // swallow network errors
38+
);
39+
}
40+
41+
#fetch(req: TemplateCreateRequest, signal?: AbortSignal): Promise<Response> {
1642
const url = `${this.opts.gatewayUrl}/api/templates`;
1743

1844
const headers: Record<string, string> = {
@@ -22,20 +48,11 @@ export class ComputeGatewayClient {
2248
headers["Authorization"] = `Bearer ${this.opts.authToken}`;
2349
}
2450

25-
const signal = options?.signal ?? AbortSignal.timeout(this.opts.timeoutMs);
26-
27-
const response = await fetch(url, {
51+
return fetch(url, {
2852
method: "POST",
2953
headers,
3054
body: JSON.stringify(req),
31-
signal,
55+
signal: signal ?? AbortSignal.timeout(this.opts.timeoutMs),
3256
});
33-
34-
if (!response.ok) {
35-
const errorBody = await response.text().catch(() => "unknown error");
36-
throw new Error(`Gateway template creation failed (${response.status}): ${errorBody}`);
37-
}
38-
39-
return { accepted: response.status === 202 };
4057
}
4158
}

0 commit comments

Comments
 (0)