Skip to content

Commit caaf1af

Browse files
authored
Add initial delay when loading python functions (#8239)
* Improve robustness of function discovery for python Anecdotally, python function discovery is flakey. We propose 2 change in this PR: 1. For python discovery, add a small initial delay for python's admin server to boot. 2. Add a request timeout to retry call to retrieve trigger information. Previously, the default timeout would've been set to OS-level TCP timeout, which in my laptop was between 20~30s. * Add changelog. * Remove per-req timeout to accomodate loading large/slow main.py. * Update changelog. * Revert timeout bump.
1 parent dad1b1a commit caaf1af

File tree

4 files changed

+32
-4
lines changed

4 files changed

+32
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
- Add initial delay when loading python functions (#8239)
12
- Enforce webframeworks enablement only on webframeworks sites (#8168)

src/deploy/functions/runtimes/discovery/index.spec.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,22 @@ describe("detectFromPort", () => {
101101
code: "ECONNREFUSED",
102102
});
103103

104+
nock("http://127.0.0.1:8080").get("/__/functions.yaml").times(3).replyWithError({
105+
message: "Almost there",
106+
code: "ETIMEDOUT",
107+
});
108+
104109
nock("http://127.0.0.1:8080").get("/__/functions.yaml").reply(200, YAML_TEXT);
105110

106111
const parsed = await discovery.detectFromPort(8080, "project", "nodejs16");
107112
expect(parsed).to.deep.equal(BUILD);
108113
});
114+
115+
it("retries when request times out", async () => {
116+
nock("http://127.0.0.1:8081").get("/__/functions.yaml").delay(1_000).reply(200, YAML_TEXT);
117+
nock("http://127.0.0.1:8080").get("/__/functions.yaml").reply(200, YAML_TEXT);
118+
119+
const parsed = await discovery.detectFromPort(8080, "project", "nodejs16", 0, 500);
120+
expect(parsed).to.deep.equal(BUILD);
121+
});
109122
});

src/deploy/functions/runtimes/discovery/index.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ export async function detectFromPort(
7575
port: number,
7676
project: string,
7777
runtime: Runtime,
78+
initialDelay = 0,
7879
timeout = 10_000 /* 10s to boot up */,
7980
): Promise<build.Build> {
8081
let res: Response;
@@ -84,13 +85,21 @@ export async function detectFromPort(
8485
}, getFunctionDiscoveryTimeout() || timeout);
8586
});
8687

88+
// Initial delay to wait for admin server to boot.
89+
if (initialDelay > 0) {
90+
await new Promise((resolve) => setTimeout(resolve, initialDelay));
91+
}
92+
93+
const url = `http://127.0.0.1:${port}/__/functions.yaml`;
8794
while (true) {
8895
try {
89-
res = await Promise.race([fetch(`http://127.0.0.1:${port}/__/functions.yaml`), timedOut]);
96+
res = await Promise.race([fetch(url), timedOut]);
9097
break;
9198
} catch (err: any) {
92-
// Allow us to wait until the server is listening.
93-
if (err?.code === "ECONNREFUSED") {
99+
if (
100+
err?.name === "FetchError" ||
101+
["ECONNREFUSED", "ECONNRESET", "ETIMEDOUT"].includes(err?.code)
102+
) {
94103
continue;
95104
}
96105
throw err;

src/deploy/functions/runtimes/python/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,12 @@ export class Delegate implements runtimes.RuntimeDelegate {
195195
});
196196
const killProcess = await this.serveAdmin(adminPort, envs);
197197
try {
198-
discovered = await discovery.detectFromPort(adminPort, this.projectId, this.runtime);
198+
discovered = await discovery.detectFromPort(
199+
adminPort,
200+
this.projectId,
201+
this.runtime,
202+
500 /* initialDelay, python startup is slow */,
203+
);
199204
} finally {
200205
await killProcess();
201206
}

0 commit comments

Comments
 (0)