Reduce startup git work for workspace snapshots

This commit is contained in:
Mohamed Boudra
2026-05-27 20:18:49 +07:00
parent dbfd42da46
commit 5d8dc800fc
13 changed files with 862 additions and 57 deletions
@@ -29,6 +29,79 @@ export interface GitCommandResult {
signal: NodeJS.Signals | null;
}
export interface GitCommandMetric {
args: string[];
cwd: string;
startedAtMs: number;
durationMs: number;
exitCode: number | null;
signal: NodeJS.Signals | null;
success: boolean;
}
export interface GitCommandMetricsSnapshot {
commands: GitCommandMetric[];
total: number;
failed: number;
maxConcurrent: number;
}
interface GitCommandMetricsState {
commands: GitCommandMetric[];
active: number;
maxConcurrent: number;
}
let gitCommandMetricsState: GitCommandMetricsState | null = null;
export function startGitCommandMetrics(): void {
gitCommandMetricsState = {
commands: [],
active: 0,
maxConcurrent: 0,
};
}
export function stopGitCommandMetrics(): GitCommandMetricsSnapshot {
const state = gitCommandMetricsState;
gitCommandMetricsState = null;
if (!state) {
return {
commands: [],
total: 0,
failed: 0,
maxConcurrent: 0,
};
}
return {
commands: [...state.commands],
total: state.commands.length,
failed: state.commands.filter((command) => !command.success).length,
maxConcurrent: state.maxConcurrent,
};
}
function beginGitCommandMetric(): GitCommandMetricsState | null {
const state = gitCommandMetricsState;
if (!state) {
return null;
}
state.active += 1;
state.maxConcurrent = Math.max(state.maxConcurrent, state.active);
return state;
}
function finishGitCommandMetric(
state: GitCommandMetricsState | null,
metric: GitCommandMetric,
): void {
if (!state) {
return;
}
state.active = Math.max(0, state.active - 1);
state.commands.push(metric);
}
function mergeEnvOverlays(
env: ProcessEnvRecord | undefined,
envOverlay: ProcessEnvRecord | undefined,
@@ -59,6 +132,7 @@ export function runGitCommand(
const command = formatGitCommand(args);
const envOverlay = mergeEnvOverlays(options.env, options.envOverlay);
const startedAt = Date.now();
const metricsState = beginGitCommandMetric();
const logger = typeof options.logger?.trace === "function" ? options.logger : undefined;
const traceContext = logger
? {
@@ -87,6 +161,7 @@ export function runGitCommand(
});
let settled = false;
let metricFinished = false;
let truncated = false;
let stdoutBytes = 0;
let stderrBytes = 0;
@@ -100,9 +175,24 @@ export function runGitCommand(
callback();
};
const finishMetricOnce = (metric: GitCommandMetric) => {
if (metricFinished) return;
metricFinished = true;
finishGitCommandMetric(metricsState, metric);
};
const timer = setTimeout(() => {
const error = new Error(`Git command timed out after ${timeout}ms: ${command}`);
child.kill("SIGKILL");
finishMetricOnce({
args,
cwd: options.cwd,
startedAtMs: startedAt,
durationMs: Date.now() - startedAt,
exitCode: null,
signal: "SIGKILL",
success: false,
});
settle(() => reject(error));
}, timeout);
@@ -147,6 +237,15 @@ export function runGitCommand(
});
child.on("error", (error) => {
finishMetricOnce({
args,
cwd: options.cwd,
startedAtMs: startedAt,
durationMs: Date.now() - startedAt,
exitCode: null,
signal: null,
success: false,
});
if (logger && traceContext) {
logger.trace(
{
@@ -184,6 +283,15 @@ export function runGitCommand(
}
if (!truncated && !acceptExitCodes.includes(exitCode ?? -1)) {
finishMetricOnce({
args,
cwd: options.cwd,
startedAtMs: startedAt,
durationMs: Date.now() - startedAt,
exitCode,
signal,
success: false,
});
const stderrPreview = result.stderr.trim() || "(no stderr)";
const truncationNote = result.truncated ? " (stdout truncated)" : "";
@@ -197,6 +305,15 @@ export function runGitCommand(
return;
}
finishMetricOnce({
args,
cwd: options.cwd,
startedAtMs: startedAt,
durationMs: Date.now() - startedAt,
exitCode,
signal,
success: true,
});
settle(() => resolve(result));
});
}),