Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/bright-pandas-profile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@cloudflare/deploy-helpers": minor
---

Share Worker startup profiling with other Cloudflare developer tools

Move the bundle analyser out of Wrangler so `cf` and deploy failure diagnostics can use the same Miniflare CPU profiler. The `analyseBundle` callback on `DeployCallbacks` is now optional and deprecated, and will be removed in a future release once all clients have been updated to stop passing this property.
10 changes: 8 additions & 2 deletions packages/deploy-helpers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
"./create-worker-upload-form": {
"import": "./dist/create-worker-upload-form.mjs",
"types": "./dist/create-worker-upload-form.d.mts"
},
"./startup-profile": {
"import": "./dist/startup-profile.mjs",
"types": "./dist/startup-profile.d.mts"
}
},
"scripts": {
Expand All @@ -47,11 +51,13 @@
"blake3-wasm": "2.1.5",
"chalk": "catalog:default",
"command-exists": "catalog:default",
"devtools-protocol": "0.0.1182435",
"dotenv": "catalog:default",
"miniflare": "workspace:*",
"p-queue": "9.0.0",
"pretty-bytes": "6.1.1",
"undici": "catalog:default"
"undici": "catalog:default",
"ws": "catalog:default"
},
"devDependencies": {
"@cloudflare/workers-shared": "workspace:*",
Expand All @@ -60,8 +66,8 @@
"@types/command-exists": "^1.2.0",
"@types/json-diff": "^1.0.3",
"@types/node": "catalog:default",
"@types/ws": "^8.5.7",
"concurrently": "^8.2.2",
"devtools-protocol": "^0.0.1182435",
"esbuild": "catalog:default",
"json-diff": "^1.0.6",
"ts-dedent": "^2.2.0",
Expand Down
4 changes: 4 additions & 0 deletions packages/deploy-helpers/scripts/deps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export const EXTERNAL_DEPENDENCIES = [
"@cloudflare/containers-shared",
"@cloudflare/workers-utils",
"miniflare",
// Public declaration files expose Chrome DevTools Protocol profile types.
"devtools-protocol",

// These are externalized to avoid duplication in wrangler's bundle,
// which already bundles these packages itself.
Expand All @@ -21,4 +23,6 @@ export const EXTERNAL_DEPENDENCIES = [
"p-queue",
"pretty-bytes",
"undici",
// WebSocket client for the local workerd inspector used by startup profiling.
"ws",
];
10 changes: 7 additions & 3 deletions packages/deploy-helpers/src/deploy/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,12 @@ export type DeployCallbacks = {
namespace: string | undefined;
}>)
| undefined;
analyseBundle:
| ((workerBundle: string | FormData) => Promise<Record<string, unknown>>)
| undefined;
/**
* @deprecated Startup profiling is provided by deploy-helpers automatically.
*/
analyseBundle?: (
workerBundle: string | FormData
) => Promise<Record<string, unknown>>;
};

type DeployResult = {
Expand Down Expand Up @@ -700,6 +703,7 @@ async function deployWorker(
dependencies,
workerBundle,
projectRoot,
// eslint-disable-next-line @typescript-eslint/no-deprecated -- compatibility callback for existing deploy-helpers consumers
callbacks.analyseBundle
);
if (message !== null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import { logger } from "../../shared/context";
import type { Metafile } from "esbuild";
import type { FormData } from "undici";

type AnalyseBundle = (bundle: FormData | string) => Promise<unknown>;

export async function helpIfErrorIsSizeOrScriptStartup(
err: unknown,
dependencies: { [path: string]: { bytesInOutput: number } },
workerBundle: FormData | string,
projectRoot: string | undefined,
analyseBundle?: (bundle: FormData | string) => Promise<unknown>
analyseBundle?: AnalyseBundle
): Promise<string | null> {
if (errIsScriptSize(err)) {
return diagnoseScriptSizeError(err, dependencies);
Expand Down Expand Up @@ -58,7 +60,7 @@ export async function diagnoseStartupError(
err: ParseError,
workerBundle: FormData | string,
projectRoot: string | undefined,
analyseBundle?: (bundle: FormData | string) => Promise<unknown>
analyseBundle: AnalyseBundle = analyseBundleLazily
Comment thread
petebacondarwin marked this conversation as resolved.
): Promise<string> {
let errorMessage = dedent`
Your Worker failed validation because it exceeded startup limits.
Expand All @@ -71,24 +73,22 @@ export async function diagnoseStartupError(
Refer to https://developers.cloudflare.com/workers/platform/limits/#worker-startup-time for more details`;

try {
if (!analyseBundle) {
return errorMessage;
}
const cpuProfile = await analyseBundle(workerBundle);
const tmpDir = await getWranglerTmpDir(
projectRoot,
"startup-profile",
false
);
const profile = path.relative(
const profilePath = path.join(tmpDir.path, "worker.cpuprofile");
const displayProfilePath = path.relative(
projectRoot ?? process.cwd(),
path.join(tmpDir.path, `worker.cpuprofile`)
profilePath
);
await writeFile(profile, JSON.stringify(cpuProfile));
await writeFile(profilePath, JSON.stringify(cpuProfile));

errorMessage += dedent`

A CPU Profile of your Worker's startup phase has been written to ${profile} - load it into the Chrome DevTools profiler (or directly in VSCode) to view a flamegraph.`;
A CPU Profile of your Worker's startup phase has been written to ${displayProfilePath} - load it into the Chrome DevTools profiler (or directly in VSCode) to view a flamegraph.`;
} catch (profilingError) {
logger.debug(
`An error occurred while trying to locally profile the Worker: ${profilingError}`
Expand All @@ -98,6 +98,13 @@ export async function diagnoseStartupError(
return errorMessage;
}

async function analyseBundleLazily(
workerBundle: FormData | string
): Promise<unknown> {
const { analyseBundle } = await import("../../startup-profile");
return analyseBundle(workerBundle);
}

/**
* Gets a message that describes the largest dependencies in the script or `null` if there are none.
*/
Expand Down
4 changes: 3 additions & 1 deletion packages/deploy-helpers/src/deploy/versions-upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ import type { RetrieveSourceMapFunction } from "./helpers/sourcemap";
import type { CfWorkerInit } from "@cloudflare/workers-utils";
import type { FormData } from "undici";

/** Compatibility callback shape for existing deploy-helpers consumers. */
export type VersionsUploadCallbacks = Pick<DeployCallbacks, "analyseBundle">;

type VersionsUploadResult = {
Expand All @@ -85,7 +86,7 @@ export default async function versionsUpload(
props: VersionsUploadProps,
config: ContainerlessConfig,
buildResult: WorkerBuildResult,
callbacks: VersionsUploadCallbacks
callbacks: VersionsUploadCallbacks = {}
): Promise<VersionsUploadResult> {
// DO NOT put anything in this function, this is just a thin wrapper to call writeOutput at the end

Expand Down Expand Up @@ -411,6 +412,7 @@ async function uploadWorkerVersion(
dependencies,
workerBundle,
projectRoot,
// eslint-disable-next-line @typescript-eslint/no-deprecated -- compatibility callback for existing deploy-helpers consumers
callbacks.analyseBundle
);
if (message) {
Expand Down
Loading
Loading