Skip to content

Commit

Permalink
Add in a Honeycomb reporter for config pushes (#29573)
Browse files Browse the repository at this point in the history
GitOrigin-RevId: 04b61c54119f90f16d972ab98744ad82e82ab257
  • Loading branch information
sujayakar authored and Convex, Inc. committed Sep 5, 2024
1 parent 479cd8a commit df462a4
Show file tree
Hide file tree
Showing 3 changed files with 335 additions and 68 deletions.
187 changes: 119 additions & 68 deletions src/cli/lib/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ import {
getFunctionsDirectoryPath,
readProjectConfig,
} from "./config.js";
import { finishPush, startPush, waitForSchema } from "./deploy2.js";
import {
finishPush,
reportPushCompleted,
startPush,
waitForSchema,
} from "./deploy2.js";
import { version } from "../version.js";
import { PushOptions, runNonComponentsPush } from "./push.js";
import { ensureHasConvexDependency, functionsDir } from "./utils/utils.js";
Expand All @@ -37,7 +42,7 @@ import { withTmpDir } from "../../bundler/fs.js";
import { ROOT_DEFINITION_FILENAME } from "./components/constants.js";
import { handleDebugBundlePath } from "./debugBundlePath.js";
import chalk from "chalk";
import { StartPushResponse } from "./deployApi/startPush.js";
import { StartPushRequest, StartPushResponse } from "./deployApi/startPush.js";
import {
deploymentSelectionFromOptions,
fetchDeploymentCredentialsProvisionProd,
Expand All @@ -46,6 +51,7 @@ import {
FinishPushDiff,
DeveloperIndexConfig,
} from "./deployApi/finishPush.js";
import { Reporter, Span } from "./tracing.js";

export async function runCodegen(ctx: Context, options: CodegenOptions) {
// This also ensures the current directory is the project root.
Expand All @@ -63,12 +69,18 @@ export async function runCodegen(ctx: Context, options: CodegenOptions) {
deploymentSelection,
);

await startComponentsPushAndCodegen(ctx, projectConfig, configPath, {
...options,
...credentials,
generateCommonJSApi: options.commonjs,
verbose: options.dryRun,
});
await startComponentsPushAndCodegen(
ctx,
Span.noop(),
projectConfig,
configPath,
{
...options,
...credentials,
generateCommonJSApi: options.commonjs,
verbose: options.dryRun,
},
);
} else {
if (options.init) {
await doInitCodegen(ctx, functionsDirectoryPath, false, {
Expand Down Expand Up @@ -104,6 +116,7 @@ export async function runPush(ctx: Context, options: PushOptions) {

async function startComponentsPushAndCodegen(
ctx: Context,
parentSpan: Span,
projectConfig: ProjectConfig,
configPath: string,
options: {
Expand Down Expand Up @@ -142,46 +155,47 @@ async function startComponentsPushAndCodegen(
// Create a list of relevant component directories. These are just for knowing
// while directories to bundle in bundleDefinitions and bundleImplementations.
// This produces a bundle in memory as a side effect but it's thrown away.
//
// This is the very first time we traverse the component graph.
// We're just traversing to discover
const { components, dependencyGraph } = await componentGraph(
ctx,
absWorkingDir,
rootComponent,
verbose,
const { components, dependencyGraph } = await parentSpan.enterAsync(
"componentGraph",
() => componentGraph(ctx, absWorkingDir, rootComponent, verbose),
);

changeSpinner(ctx, "Generating server code...");
await withTmpDir(async (tmpDir) => {
await doInitialComponentCodegen(ctx, tmpDir, rootComponent, options);
for (const directory of components.values()) {
await doInitialComponentCodegen(ctx, tmpDir, directory, options);
}
});
await parentSpan.enterAsync("doInitialComponentCodegen", () =>
withTmpDir(async (tmpDir) => {
await doInitialComponentCodegen(ctx, tmpDir, rootComponent, options);
for (const directory of components.values()) {
await doInitialComponentCodegen(ctx, tmpDir, directory, options);
}
}),
);

changeSpinner(ctx, "Bundling component definitions...");
// This bundles everything but the actual function definitions
const {
appDefinitionSpecWithoutImpls,
componentDefinitionSpecsWithoutImpls,
} = await bundleDefinitions(
ctx,
absWorkingDir,
dependencyGraph,
rootComponent,
// Note that this *includes* the root component.
[...components.values()],
} = await parentSpan.enterAsync("bundleDefinitions", () =>
bundleDefinitions(
ctx,
absWorkingDir,
dependencyGraph,
rootComponent,
// Note that this *includes* the root component.
[...components.values()],
),
);

changeSpinner(ctx, "Bundling component schemas and implementations...");
const { appImplementation, componentImplementations } =
await bundleImplementations(
ctx,
rootComponent,
[...components.values()],
projectConfig.node.externalPackages,
verbose,
await parentSpan.enterAsync("bundleImplementations", () =>
bundleImplementations(
ctx,
rootComponent,
[...components.values()],
projectConfig.node.externalPackages,
verbose,
),
);
if (options.debugBundlePath) {
const { config: localConfig } = await configFromProjectConfig(
Expand Down Expand Up @@ -246,55 +260,84 @@ async function startComponentsPushAndCodegen(
);
return null;
}
logStartPushSizes(parentSpan, startPushRequest);

changeSpinner(ctx, "Uploading functions to Convex...");
const startPushResponse = await startPush(
ctx,
options.url,
startPushRequest,
verbose,
const startPushResponse = await parentSpan.enterAsync("startPush", (span) =>
startPush(ctx, span, options.url, startPushRequest, verbose),
);

verbose && console.log("startPush:");
verbose && console.dir(startPushResponse, { depth: null });

changeSpinner(ctx, "Generating TypeScript bindings...");
await withTmpDir(async (tmpDir) => {
await doFinalComponentCodegen(
ctx,
tmpDir,
rootComponent,
rootComponent,
startPushResponse,
options,
);
for (const directory of components.values()) {
await parentSpan.enterAsync("doFinalComponentCodegen", () =>
withTmpDir(async (tmpDir) => {
await doFinalComponentCodegen(
ctx,
tmpDir,
rootComponent,
directory,
rootComponent,
startPushResponse,
options,
);
}
});
for (const directory of components.values()) {
await doFinalComponentCodegen(
ctx,
tmpDir,
rootComponent,
directory,
startPushResponse,
options,
);
}
}),
);

changeSpinner(ctx, "Running TypeScript...");
await typeCheckFunctionsInMode(ctx, options.typecheck, rootComponent.path);
for (const directory of components.values()) {
await typeCheckFunctionsInMode(ctx, options.typecheck, directory.path);
}
await parentSpan.enterAsync("typeCheckFunctionsInMode", async () => {
await typeCheckFunctionsInMode(ctx, options.typecheck, rootComponent.path);
for (const directory of components.values()) {
await typeCheckFunctionsInMode(ctx, options.typecheck, directory.path);
}
});

return startPushResponse;
}

function logStartPushSizes(span: Span, startPushRequest: StartPushRequest) {
let v8Size = 0;
let v8Count = 0;
let nodeSize = 0;
let nodeCount = 0;

for (const componentDefinition of startPushRequest.componentDefinitions) {
for (const module of componentDefinition.functions) {
if (module.environment === "isolate") {
v8Size += module.source.length + (module.sourceMap ?? "").length;
v8Count += 1;
} else if (module.environment === "node") {
nodeSize += module.source.length + (module.sourceMap ?? "").length;
nodeCount += 1;
}
}
}
span.setProperty("v8_size", v8Size.toString());
span.setProperty("v8_count", v8Count.toString());
span.setProperty("node_size", nodeSize.toString());
span.setProperty("node_count", nodeCount.toString());
}

export async function runComponentsPush(
ctx: Context,
options: PushOptions,
configPath: string,
projectConfig: ProjectConfig,
) {
const reporter = new Reporter();
const pushSpan = Span.root(reporter, "runComponentsPush");
pushSpan.setProperty("cli_version", version);

await ensureHasConvexDependency(ctx, "push");

if (options.dryRun) {
Expand All @@ -305,25 +348,33 @@ export async function runComponentsPush(
});
}

const startPushResponse = await startComponentsPushAndCodegen(
ctx,
projectConfig,
configPath,
options,
const startPushResponse = await pushSpan.enterAsync(
"startComponentsPushAndCodegen",
(span) =>
startComponentsPushAndCodegen(
ctx,
span,
projectConfig,
configPath,
options,
),
);
if (!startPushResponse) {
return;
}

await waitForSchema(ctx, options.adminKey, options.url, startPushResponse);
await pushSpan.enterAsync("waitForSchema", (span) =>
waitForSchema(ctx, span, options.adminKey, options.url, startPushResponse),
);

const finishPushResponse = await finishPush(
ctx,
options.adminKey,
options.url,
startPushResponse,
const finishPushResponse = await pushSpan.enterAsync("finishPush", (span) =>
finishPush(ctx, span, options.adminKey, options.url, startPushResponse),
);
printDiff(ctx, finishPushResponse, options);
pushSpan.end();

// Asynchronously report that the push completed.
void reportPushCompleted(ctx, options.adminKey, options.url, reporter);
}

function printDiff(
Expand Down
37 changes: 37 additions & 0 deletions src/cli/lib/deploy2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@ import chalk from "chalk";
import { getTargetDeploymentName } from "./deployment.js";
import { deploymentDashboardUrlPage } from "../dashboard.js";
import { finishPushDiff, FinishPushDiff } from "./deployApi/finishPush.js";
import { Reporter, Span } from "./tracing.js";

/** Push configuration2 to the given remote origin. */
export async function startPush(
ctx: Context,
span: Span,
url: string,
request: StartPushRequest,
verbose?: boolean,
Expand All @@ -49,6 +51,9 @@ export async function startPush(
const response = await fetch("/api/deploy2/start_push", {
body: JSON.stringify(request),
method: "POST",
headers: {
traceparent: span.encodeW3CTraceparent(),
},
});
return startPushResponse.parse(await response.json());
} catch (error: unknown) {
Expand Down Expand Up @@ -90,6 +95,7 @@ const SCHEMA_TIMEOUT_MS = 10_000;

export async function waitForSchema(
ctx: Context,
span: Span,
adminKey: string,
url: string,
startPush: StartPushResponse,
Expand All @@ -111,6 +117,9 @@ export async function waitForSchema(
timeoutMs: SCHEMA_TIMEOUT_MS,
}),
method: "POST",
headers: {
traceparent: span.encodeW3CTraceparent(),
},
});
currentStatus = schemaStatus.parse(await response.json());
} catch (error: unknown) {
Expand Down Expand Up @@ -182,6 +191,7 @@ export async function waitForSchema(

export async function finishPush(
ctx: Context,
span: Span,
adminKey: string,
url: string,
startPush: StartPushResponse,
Expand All @@ -196,6 +206,9 @@ export async function finishPush(
dryRun: false,
}),
method: "POST",
headers: {
traceparent: span.encodeW3CTraceparent(),
},
});
return finishPushDiff.parse(await response.json());
} catch (error: unknown) {
Expand All @@ -212,3 +225,27 @@ export type AppDefinitionConfigWithoutImpls = Omit<
AppDefinitionConfig,
"schema" | "functions" | "auth"
>;

export async function reportPushCompleted(
ctx: Context,
adminKey: string,
url: string,
reporter: Reporter,
) {
const fetch = deploymentFetch(url, adminKey);
try {
const response = await fetch("/api/deploy2/report_push_completed", {
body: JSON.stringify({
adminKey,
spans: reporter.spans,
}),
method: "POST",
});
await response.json();
} catch (error: unknown) {
logFailure(
ctx,
"Error: Unable to report push completed to " + url + ": " + error,
);
}
}
Loading

0 comments on commit df462a4

Please sign in to comment.