History

Serve the worker-bundler artifact from static assets (#1378)

The publish bundler artifact, worker-bundler source and esbuild wasm, is fetched from static assets at first use instead of being inlined into the worker script, returning the script to its previous size. Cold-start latency regressed while the 19MB artifact chunk was part of the script blob.

Rhys Sullivan committed 4 months ago

Commit 56b60bf1 parent 63e19e78 files · +175 −18Browse files

Showing the changes to apps/cloud/scripts (2 of 8 files). Show all

Unified · Split

2 files
  1. apps/cloud/scripts/assert-worker-bundler-assets.mjs +54 −0
  2. apps/cloud/scripts/build.mjs +1 −0
apps/cloud/scripts/assert-worker-bundler-assets.mjs+54−0
@@ -0,0 +1,54 @@
1import { readdirSync, readFileSync, statSync } from "node:fs";
2import { join } from "node:path";
3
4const appRoot = new URL("..", import.meta.url);
5const serverAssetsDir = new URL("dist/server/assets/", appRoot);
6const clientAssetsDir = new URL("dist/client/assets/", appRoot);
7const largeFileLimit = 3 * 1024 * 1024;
8const sourceAssetMinimum = 512 * 1024;
9
10const fail = (message) => {
11 // oxlint-disable-next-line executor/no-try-catch-or-throw, executor/no-error-constructor -- boundary: build assertion script reports a failed artifact contract
12 throw new Error(message);
13};
14
15const fileSize = (dir, name) => statSync(join(dir.pathname, name)).size;
16
17const serverAssets = readdirSync(serverAssetsDir);
18for (const name of serverAssets) {
19 const path = join(serverAssetsDir.pathname, name);
20 const size = statSync(path).size;
21 if (size <= largeFileLimit) continue;
22 if (name.includes("worker-bundler")) {
23 fail(`server assets include oversized worker-bundler chunk ${name}`);
24 }
25 const sample = readFileSync(path, "utf8");
26 if (sample.includes("worker-bundler")) {
27 fail(`server assets include worker-bundler content in oversized chunk ${name}`);
28 }
29}
30
31const clientAssets = readdirSync(clientAssetsDir);
32const source = clientAssets.find((name) => /^worker-bundler-source-[a-f0-9]{12}\.js$/.test(name));
33const wasm = clientAssets.find((name) =>
34 /^worker-bundler-esbuild-wasm-[a-f0-9]{12}\.wasm$/.test(name),
35);
36
37if (source === undefined) fail("missing worker-bundler source asset");
38if (wasm === undefined) fail("missing worker-bundler esbuild wasm asset");
39
40const sourceSize = fileSize(clientAssetsDir, source);
41const wasmSize = fileSize(clientAssetsDir, wasm);
42if (sourceSize <= sourceAssetMinimum) {
43 fail(`worker-bundler source asset is unexpectedly small: ${source}`);
44}
45if (wasmSize <= largeFileLimit) fail(`worker-bundler wasm asset is unexpectedly small: ${wasm}`);
46
47const wasmMagic = readFileSync(join(clientAssetsDir.pathname, wasm)).subarray(0, 4);
48if (!wasmMagic.equals(Buffer.from([0x00, 0x61, 0x73, 0x6d]))) {
49 fail(`worker-bundler wasm asset does not start with wasm magic bytes: ${wasm}`);
50}
51
52console.log(
53 `[assert-worker-bundler-assets] ${source} (${sourceSize} bytes), ${wasm} (${wasmSize} bytes)`,
54);
apps/cloud/scripts/build.mjs+1−0
@@ -24,6 +24,7 @@ const steps = [ · 23 unmodified lines
24 // imports, so they must be built before vite starts.24 // imports, so they must be built before vite starts.
25 "turbo run build --filter @executor-js/vite-plugin --filter @executor-js/react --filter @executor-js/plugin-apps",25 "turbo run build --filter @executor-js/vite-plugin --filter @executor-js/react --filter @executor-js/plugin-apps",
26 "vite build",26 "vite build",
27 "node scripts/assert-worker-bundler-assets.mjs",
27];28];
28 29
29for (const step of steps) {30for (const step of steps) {