From 939631b2b80bd15494e5aa8e2bbd001259edac17 Mon Sep 17 00:00:00 2001 From: Vance Ingalls Date: Wed, 17 Jun 2026 23:30:24 -0700 Subject: [PATCH] feat(producer): host/render telemetry in RenderPerfSummary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a `host` block (platform, arch, cpuCount, totalMemMb, nodeVersion, gpuDisabled) to RenderPerfSummary so fleet-wide telemetry can correlate render performance with the machine it ran on — chiefly cpuCount vs the existing `workers` field (core over/under-subscription) and totalMemMb vs lowMemoryMode / single-worker collapse. Capture mode + GPU mode already surface via `observability`; this fills in the missing host facts. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/services/render/perfSummary.test.ts | 45 +++++++++++++++++++ .../src/services/render/perfSummary.ts | 11 +++++ .../src/services/renderOrchestrator.ts | 22 +++++++++ 3 files changed, 78 insertions(+) create mode 100644 packages/producer/src/services/render/perfSummary.test.ts diff --git a/packages/producer/src/services/render/perfSummary.test.ts b/packages/producer/src/services/render/perfSummary.test.ts new file mode 100644 index 0000000000..257d00095c --- /dev/null +++ b/packages/producer/src/services/render/perfSummary.test.ts @@ -0,0 +1,45 @@ +import { describe, it, expect } from "vitest"; +import { buildRenderPerfSummary } from "./perfSummary.js"; +import type { RenderJob } from "../renderOrchestrator.js"; + +// ponytail: minimal stub — only the fields buildRenderPerfSummary reads are real. +const baseInput = { + job: { id: "r1", config: { fps: { num: 30, den: 1 }, quality: "high" } } as unknown as RenderJob, + workerCount: 4, + enableChunkedEncode: false, + chunkedEncodeSize: 0, + compositionDurationSeconds: 5, + totalFrames: 150, + outputWidth: 1920, + outputHeight: 1080, + videoCount: 0, + audioCount: 0, + totalElapsedMs: 1234, + perfStages: {}, + videoExtractBreakdown: undefined, + tmpPeakBytes: 0, + captureAttempts: [], + hdrDiagnostics: { videoExtractionFailures: 0, imageDecodeFailures: 0 }, + peakRssBytes: 0, + peakHeapUsedBytes: 0, +}; + +describe("buildRenderPerfSummary host telemetry", () => { + it("captures host facts and threads gpuDisabled through", () => { + const summary = buildRenderPerfSummary({ ...baseInput, gpuDisabled: true }); + expect(summary.host).toBeDefined(); + expect(summary.host?.gpuDisabled).toBe(true); + expect(summary.host?.cpuCount).toBeGreaterThan(0); + expect(summary.host?.totalMemMb).toBeGreaterThan(0); + expect(summary.host?.platform).toBe(process.platform); + expect(summary.host?.arch).toBe(process.arch); + expect(summary.host?.nodeVersion).toBe(process.version); + }); + + it("reflects gpuDisabled=false", () => { + const summary = buildRenderPerfSummary({ ...baseInput, gpuDisabled: false }); + expect(summary.host?.gpuDisabled).toBe(false); + // workers vs cores is the headline correlation — both must be present. + expect(summary.workers).toBe(4); + }); +}); diff --git a/packages/producer/src/services/render/perfSummary.ts b/packages/producer/src/services/render/perfSummary.ts index cce8e69710..c67b10c9b0 100644 --- a/packages/producer/src/services/render/perfSummary.ts +++ b/packages/producer/src/services/render/perfSummary.ts @@ -3,6 +3,7 @@ * the `perf-summary.json` debug artifact. */ +import { arch, cpus, platform, totalmem } from "node:os"; import { fpsToNumber } from "@hyperframes/core"; import type { CaptureCalibrationSample, CaptureCostEstimate } from "./captureCost.js"; import type { @@ -39,6 +40,8 @@ export function buildRenderPerfSummary(input: { observability?: RenderObservabilitySummary; peakRssBytes: number; peakHeapUsedBytes: number; + /** `cfg.disableGpu` — the hard `--disable-gpu` flag, for the `host` GPU picture. */ + gpuDisabled: boolean; }): RenderPerfSummary { return { renderId: input.job.id, @@ -82,5 +85,13 @@ export function buildRenderPerfSummary(input: { : undefined, peakRssMb: Math.round(input.peakRssBytes / (1024 * 1024)), peakHeapUsedMb: Math.round(input.peakHeapUsedBytes / (1024 * 1024)), + host: { + platform: platform(), + arch: arch(), + cpuCount: cpus().length, + totalMemMb: Math.round(totalmem() / (1024 * 1024)), + nodeVersion: process.version, + gpuDisabled: input.gpuDisabled, + }, }; } diff --git a/packages/producer/src/services/renderOrchestrator.ts b/packages/producer/src/services/renderOrchestrator.ts index 99137f5d4d..019a36b236 100644 --- a/packages/producer/src/services/renderOrchestrator.ts +++ b/packages/producer/src/services/renderOrchestrator.ts @@ -326,6 +326,27 @@ export interface RenderPerfSummary { peakHeapUsedMb?: number; hdrDiagnostics?: HdrDiagnostics; hdrPerf?: HdrPerfSummary; + /** + * Render-host facts, captured from the orchestrator process. Lets fleet-wide + * telemetry correlate render performance with the machine it ran on — most + * importantly `cpuCount` vs the top-level `workers` (are we under-/over- + * subscribing cores?) and `totalMemMb` (does `lowMemoryMode` / single-worker + * collapse track real memory pressure?). `gpuDisabled` completes the GPU + * picture alongside `observability.browserGpuMode`. + * + * Reflects the ORCHESTRATOR host. For distributed renders the chunk workers + * may run on different machines; this is still the right signal for the + * common single-machine render. Optional for back-compat with serialized + * older summaries. + */ + host?: { + platform: string; + arch: string; + cpuCount: number; + totalMemMb: number; + nodeVersion: string; + gpuDisabled: boolean; + }; } export interface HdrDiagnostics { @@ -1639,6 +1660,7 @@ export async function executeRenderJob( observability: observabilitySummary, peakRssBytes: memSampler.peakRssBytes(), peakHeapUsedBytes: memSampler.peakHeapUsedBytes(), + gpuDisabled: cfg.disableGpu, }); job.perfSummary = perfSummary; if (job.config.debug) {