From 86d6d65cd948ef41a3fb63e4b4c0571760b2f672 Mon Sep 17 00:00:00 2001 From: eleanorjboyd <26030610+eleanorjboyd@users.noreply.github.com> Date: Fri, 29 May 2026 09:09:56 -0700 Subject: [PATCH 1/2] feat: add PET version and build ID telemetry properties to enhance performance tracking --- src/common/telemetry/constants.ts | 18 ++++++++ src/managers/common/nativePythonFinder.ts | 50 ++++++++++++++++++++++- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/src/common/telemetry/constants.ts b/src/common/telemetry/constants.ts index 35f28217..546899d5 100644 --- a/src/common/telemetry/constants.ts +++ b/src/common/telemetry/constants.ts @@ -560,6 +560,8 @@ export interface IEventNamePropertyMapping { "breakdownGlobalVirtualEnvs": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true, "owner": "eleanorjboyd" }, "breakdownWorkspaces": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true, "owner": "eleanorjboyd" }, "locatorsJson": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "owner": "eleanorjboyd" }, + "petVersion": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "owner": "eleanorjboyd" }, + "petBuildId": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "owner": "eleanorjboyd" }, "": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "owner": "eleanorjboyd" } } */ @@ -582,6 +584,10 @@ export interface IEventNamePropertyMapping { breakdownWorkspaces?: number; /** JSON-serialized Record. Parse with parse_json() in Kusto. */ locatorsJson?: string; + /** PET crate version reported by the `info` RPC. 'unknown' if the call failed or the PET binary doesn't implement it. */ + petVersion?: string; + /** PET build identifier (git SHA) reported by the `info` RPC. 'unknown' if unavailable. */ + petBuildId?: string; }; /* __GDPR__ @@ -606,6 +612,8 @@ export interface IEventNamePropertyMapping { "result": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "eleanorjboyd" }, "errorType": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "eleanorjboyd" }, "triggerReason": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "eleanorjboyd" }, + "petVersion": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "owner": "eleanorjboyd" }, + "petBuildId": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "owner": "eleanorjboyd" }, "": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "owner": "eleanorjboyd" } } */ @@ -621,17 +629,27 @@ export interface IEventNamePropertyMapping { * start_failed | unknown. */ triggerReason: string; + /** PET crate version reported by the `info` RPC. 'unknown' if the call failed or the PET binary doesn't implement it. */ + petVersion?: string; + /** PET build identifier (git SHA) reported by the `info` RPC. 'unknown' if unavailable. */ + petBuildId?: string; }; /* __GDPR__ "pet.resolve": { "result": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "eleanorjboyd" }, "errorType": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "eleanorjboyd" }, + "petVersion": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "owner": "eleanorjboyd" }, + "petBuildId": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "owner": "eleanorjboyd" }, "": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "owner": "eleanorjboyd" } } */ [EventNames.PET_RESOLVE]: { result: 'success' | 'timeout' | 'error'; errorType?: string; + /** PET crate version reported by the `info` RPC. 'unknown' if the call failed or the PET binary doesn't implement it. */ + petVersion?: string; + /** PET build identifier (git SHA) reported by the `info` RPC. 'unknown' if unavailable. */ + petBuildId?: string; }; } diff --git a/src/managers/common/nativePythonFinder.ts b/src/managers/common/nativePythonFinder.ts index 33ebbe3c..1fe0a05e 100644 --- a/src/managers/common/nativePythonFinder.ts +++ b/src/managers/common/nativePythonFinder.ts @@ -24,6 +24,7 @@ const CONFIGURE_TIMEOUT_MS = 30_000; // 30 seconds for configuration const MAX_CONFIGURE_TIMEOUT_MS = 60_000; // Max configure timeout after retries (60s) const REFRESH_TIMEOUT_MS = 30_000; // 30 seconds for full refresh (with 1 retry = 60s max) const RESOLVE_TIMEOUT_MS = 30_000; // 30 seconds for single resolve +const INFO_TIMEOUT_MS = 2_000; // info is a const lookup on PET; 2s is generous // CLI fallback timeout: generous budget since it's a full process spawn doing a full scan const CLI_FALLBACK_TIMEOUT_MS = 120_000; // 2 minutes @@ -265,6 +266,12 @@ interface PetTelemetryNotification { }; } +/** Response shape of the PET `info` JSON-RPC request. */ +interface NativePetInfo { + petVersion: string; + buildId?: string; +} + /** * Error thrown when a JSON-RPC request times out. */ @@ -322,6 +329,8 @@ class NativePythonFinderImpl implements NativePythonFinder { private isRestarting: boolean = false; private processExitReason: string | undefined = undefined; private readonly configureRetry = new ConfigureRetryState(); + /** Cached PET `info` response for the current process; cleared on every start(). */ + private petInfo: NativePetInfo | undefined; constructor( private readonly outputChannel: LogOutputChannel, @@ -353,7 +362,10 @@ class NativePythonFinderImpl implements NativePythonFinder { this.outputChannel.info(`Resolved Python Environment ${environment.executable}`); // Reset restart attempts on successful request this.restartAttempts = 0; - sendTelemetryEvent(EventNames.PET_RESOLVE, sw.elapsedTime, { result: 'success' }); + sendTelemetryEvent(EventNames.PET_RESOLVE, sw.elapsedTime, { + result: 'success', + ...this.getPetInfoProperties(), + }); return environment; } catch (ex) { // On resolve timeout or connection error (not configure — configure handles its own timeout), @@ -376,6 +388,7 @@ class NativePythonFinderImpl implements NativePythonFinder { { result: errorType === 'spawn_timeout' ? 'timeout' : 'error', errorType, + ...this.getPetInfoProperties(), }, ex instanceof Error ? ex : undefined, ); @@ -460,6 +473,7 @@ class NativePythonFinderImpl implements NativePythonFinder { attempt, result: 'success', triggerReason, + ...this.getPetInfoProperties(), }); // Reset restart attempts on successful start (process didn't immediately fail) @@ -468,7 +482,13 @@ class NativePythonFinderImpl implements NativePythonFinder { sendTelemetryEvent( EventNames.PET_PROCESS_RESTART, sw.elapsedTime, - { attempt, result: 'error', errorType: classifyError(ex), triggerReason }, + { + attempt, + result: 'error', + errorType: classifyError(ex), + triggerReason, + ...this.getPetInfoProperties(), + }, ex instanceof Error ? ex : undefined, ); this.outputChannel.error('[pet] Failed to restart Python Environment Tools:', ex); @@ -701,9 +721,33 @@ class NativePythonFinderImpl implements NativePythonFinder { ); connection.listen(); + + // Stamp PET telemetry with version/buildId. Fire-and-forget — must not block refresh. + this.petInfo = undefined; + this.kickoffInfoFetch(connection); + return connection; } + private kickoffInfoFetch(connection: rpc.MessageConnection): void { + sendRequestWithTimeout(connection, 'info', {}, INFO_TIMEOUT_MS) + .then((result) => { + this.petInfo = result; + this.outputChannel.debug(`[pet] info:`, result); + }) + .catch((ex) => { + // Older PET binaries don't implement `info`; leave petInfo undefined so telemetry reports 'unknown'. + this.outputChannel.debug('[pet] info request failed:', ex); + }); + } + + private getPetInfoProperties(): { petVersion: string; petBuildId: string } { + return { + petVersion: this.petInfo?.petVersion ?? 'unknown', + petBuildId: this.petInfo?.buildId ?? 'unknown', + }; + } + private async doRefresh(options?: NativePythonEnvironmentKind | Uri[]): Promise { let lastError: unknown; @@ -840,6 +884,7 @@ class NativePythonFinderImpl implements NativePythonFinder { searchPathCount, attempt, locatorsJson: refreshPerf ? JSON.stringify(refreshPerf.locators) : undefined, + ...this.getPetInfoProperties(), }, ); } catch (ex) { @@ -853,6 +898,7 @@ class NativePythonFinderImpl implements NativePythonFinder { unresolvedCount, attempt, errorType, + ...this.getPetInfoProperties(), }, ex instanceof Error ? ex : undefined, ); From 2918944f12c560fb7b5775b5105d4458502d979f Mon Sep 17 00:00:00 2001 From: Eleanor Boyd <26030610+eleanorjboyd@users.noreply.github.com> Date: Fri, 29 May 2026 14:59:52 -0700 Subject: [PATCH 2/2] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/managers/common/nativePythonFinder.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/managers/common/nativePythonFinder.ts b/src/managers/common/nativePythonFinder.ts index 1fe0a05e..e18cefef 100644 --- a/src/managers/common/nativePythonFinder.ts +++ b/src/managers/common/nativePythonFinder.ts @@ -732,10 +732,16 @@ class NativePythonFinderImpl implements NativePythonFinder { private kickoffInfoFetch(connection: rpc.MessageConnection): void { sendRequestWithTimeout(connection, 'info', {}, INFO_TIMEOUT_MS) .then((result) => { + if (connection !== this.connection) { + return; + } this.petInfo = result; - this.outputChannel.debug(`[pet] info:`, result); + this.outputChannel.debug('[pet] info:', result); }) .catch((ex) => { + if (connection !== this.connection) { + return; + } // Older PET binaries don't implement `info`; leave petInfo undefined so telemetry reports 'unknown'. this.outputChannel.debug('[pet] info request failed:', ex); });