Skip to content

Commit 50373a7

Browse files
committed
fix(telemetry): instrument delta upgrade with spans and error capture
Delta upgrade failures were completely invisible in Sentry: - No spans for the delta attempt (only DB queries visible in traces) - Errors caught and logged at debug level (invisible without --verbose) - No captureException — errors never reported to Sentry This made it impossible to diagnose the ETXTBSY/SIGKILL issues (PRs #339, #340, #343) from telemetry alone — they were found through code analysis and local reproduction. Changes: - Wrap attemptDeltaUpgrade in withTracingSpan for a 'upgrade.delta' span - Record delta.from_version, delta.to_version, delta.channel as attributes - On success: record patch_bytes and sha256 prefix - On unavailable (no patch): record delta.result='unavailable' - On error: captureException with warning level + delta context tags, record delta.result='error' and delta.error message on span - Upgrade log.debug to log.warn for failure messages so users see them Now delta failures will appear as: 1. A span in the upgrade trace (with error status + attributes) 2. A warning-level exception in Sentry Issues (with delta context) 3. A visible stderr message to the user
1 parent 946ecd3 commit 50373a7

File tree

1 file changed

+65
-15
lines changed

1 file changed

+65
-15
lines changed

src/lib/delta-upgrade.ts

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
import { unlinkSync } from "node:fs";
2121

22+
import { captureException } from "@sentry/bun";
23+
2224
import {
2325
GITHUB_RELEASES_URL,
2426
getPlatformBinaryName,
@@ -34,6 +36,7 @@ import {
3436
type OciManifest,
3537
} from "./ghcr.js";
3638
import { logger } from "./logger.js";
39+
import { withTracingSpan } from "./telemetry.js";
3740

3841
/** Scoped logger for delta upgrade operations */
3942
const log = logger.withTag("delta-upgrade");
@@ -563,29 +566,76 @@ export async function resolveNightlyChain(
563566
* @param destPath - Path to write the patched binary
564567
* @returns Delta result with SHA-256 and size info, or null if delta is unavailable
565568
*/
566-
export async function attemptDeltaUpgrade(
569+
export function attemptDeltaUpgrade(
567570
targetVersion: string,
568571
oldBinaryPath: string,
569572
destPath: string
570573
): Promise<DeltaResult | null> {
571574
if (!canAttemptDelta(targetVersion)) {
572-
return null;
575+
return Promise.resolve(null);
573576
}
574577

575-
log.debug(`Attempting delta upgrade from ${CLI_VERSION} to ${targetVersion}`);
578+
const channel = isNightlyVersion(targetVersion) ? "nightly" : "stable";
576579

577-
try {
578-
if (isNightlyVersion(targetVersion)) {
579-
return await resolveNightlyDelta(targetVersion, oldBinaryPath, destPath);
580-
}
581-
return await resolveStableDelta(targetVersion, oldBinaryPath, destPath);
582-
} catch (error) {
583-
// Any error during delta upgrade → fall back to full download.
584-
// Log at debug so --verbose reveals the root cause (ETXTBSY, network, etc.)
585-
const msg = error instanceof Error ? error.message : String(error);
586-
log.debug(`Delta upgrade failed (${msg}), falling back to full download`);
587-
return null;
588-
}
580+
return withTracingSpan(
581+
"delta-upgrade",
582+
"upgrade.delta",
583+
async (span) => {
584+
span.setAttribute("delta.from_version", CLI_VERSION);
585+
span.setAttribute("delta.to_version", targetVersion);
586+
span.setAttribute("delta.channel", channel);
587+
588+
log.debug(
589+
`Attempting delta upgrade from ${CLI_VERSION} to ${targetVersion}`
590+
);
591+
592+
try {
593+
const result =
594+
channel === "nightly"
595+
? await resolveNightlyDelta(targetVersion, oldBinaryPath, destPath)
596+
: await resolveStableDelta(targetVersion, oldBinaryPath, destPath);
597+
598+
if (result) {
599+
span.setAttribute("delta.patch_bytes", result.patchBytes);
600+
span.setAttribute("delta.sha256", result.sha256.slice(0, 12));
601+
span.setStatus({ code: 1 }); // OK
602+
} else {
603+
// No patch available — not an error, just unavailable
604+
span.setAttribute("delta.result", "unavailable");
605+
span.setStatus({ code: 1 }); // OK — graceful fallback
606+
}
607+
return result;
608+
} catch (error) {
609+
// Record the error in Sentry so we can see delta failures in telemetry.
610+
// Marked non-fatal: the upgrade continues via full download.
611+
captureException(error, {
612+
level: "warning",
613+
tags: {
614+
"delta.from_version": CLI_VERSION,
615+
"delta.to_version": targetVersion,
616+
"delta.channel": channel,
617+
},
618+
contexts: {
619+
delta_upgrade: {
620+
from_version: CLI_VERSION,
621+
to_version: targetVersion,
622+
channel,
623+
old_binary_path: oldBinaryPath,
624+
},
625+
},
626+
});
627+
628+
const msg = error instanceof Error ? error.message : String(error);
629+
log.warn(
630+
`Delta upgrade failed (${msg}), falling back to full download`
631+
);
632+
span.setAttribute("delta.result", "error");
633+
span.setAttribute("delta.error", msg);
634+
return null;
635+
}
636+
},
637+
{ "delta.channel": channel }
638+
);
589639
}
590640

591641
/**

0 commit comments

Comments
 (0)