44import { Uri } from 'vscode' ;
55import { GetEnvironmentScope , PythonEnvironment , PythonEnvironmentApi } from '../../api' ;
66import { traceError , traceVerbose , traceWarn } from '../../common/logging' ;
7+ import { StopWatch } from '../../common/stopWatch' ;
8+ import { EventNames } from '../../common/telemetry/constants' ;
9+ import { sendTelemetryEvent } from '../../common/telemetry/sender' ;
710import { createDeferred , Deferred } from '../../common/utils/deferred' ;
811
912/**
@@ -26,6 +29,8 @@ export interface FastPathOptions {
2629 resolve : ( persistedPath : string ) => Promise < PythonEnvironment | undefined > ;
2730 /** Starts background initialization (full discovery). Returns a promise that completes when init is done. */
2831 startBackgroundInit : ( ) => Promise < void > | Thenable < void > ;
32+ /** Optional: reads the persisted env path for global scope (when scope is undefined). */
33+ getGlobalPersistedPath ?: ( ) => Promise < string | undefined > ;
2934}
3035
3136/**
@@ -52,7 +57,10 @@ export function getProjectFsPathForScope(api: Pick<PythonEnvironmentApi, 'getPyt
5257 * to fall through to the normal init path.
5358 */
5459export async function tryFastPathGet ( opts : FastPathOptions ) : Promise < FastPathResult | undefined > {
55- if ( ! ( opts . scope instanceof Uri ) ) {
60+ const isGlobalScope = ! ( opts . scope instanceof Uri ) ;
61+
62+ // Global scope is only supported when the caller provides getGlobalPersistedPath
63+ if ( isGlobalScope && ! opts . getGlobalPersistedPath ) {
5664 return undefined ;
5765 }
5866
@@ -82,23 +90,66 @@ export async function tryFastPathGet(opts: FastPathOptions): Promise<FastPathRes
8290 }
8391 }
8492
85- const fsPath = opts . getProjectFsPath ( opts . scope ) ;
86- const persistedPath = await opts . getPersistedPath ( fsPath ) ;
93+ // Look up the persisted path — either from workspace cache or global cache
94+ if ( isGlobalScope ) {
95+ // Safe: guarded by the early return above
96+ const getGlobalPersistedPath = opts . getGlobalPersistedPath as ( ) => Promise < string | undefined > ;
8797
88- if ( persistedPath ) {
89- try {
90- const resolved = await opts . resolve ( persistedPath ) ;
91- if ( resolved ) {
92- return { env : resolved } ;
98+ // Track end-to-end cross-session cache performance for global scope, including persisted-path lookup.
99+ const cacheStopWatch = new StopWatch ( ) ;
100+ const persistedPath = await getGlobalPersistedPath ( ) ;
101+
102+ if ( persistedPath ) {
103+ try {
104+ const resolved = await opts . resolve ( persistedPath ) ;
105+ if ( resolved ) {
106+ sendTelemetryEvent ( EventNames . GLOBAL_ENV_CACHE , cacheStopWatch . elapsedTime , {
107+ managerLabel : opts . label ,
108+ result : 'hit' ,
109+ } ) ;
110+ return { env : resolved } ;
111+ }
112+ // Cached path found but resolve returned undefined (e.g., Python was uninstalled)
113+ sendTelemetryEvent ( EventNames . GLOBAL_ENV_CACHE , cacheStopWatch . elapsedTime , {
114+ managerLabel : opts . label ,
115+ result : 'stale' ,
116+ } ) ;
117+ } catch ( err ) {
118+ sendTelemetryEvent ( EventNames . GLOBAL_ENV_CACHE , cacheStopWatch . elapsedTime , {
119+ managerLabel : opts . label ,
120+ result : 'stale' ,
121+ } ) ;
122+ traceWarn (
123+ `[${ opts . label } ] Fast path resolve failed for '${ persistedPath } ', falling back to full init:` ,
124+ err ,
125+ ) ;
93126 }
94- } catch ( err ) {
95- traceWarn (
96- `[${ opts . label } ] Fast path resolve failed for '${ persistedPath } ', falling back to full init:` ,
97- err ,
98- ) ;
127+ } else {
128+ sendTelemetryEvent ( EventNames . GLOBAL_ENV_CACHE , cacheStopWatch . elapsedTime , {
129+ managerLabel : opts . label ,
130+ result : 'miss' ,
131+ } ) ;
132+ traceVerbose ( `[${ opts . label } ] Fast path: no persisted path, falling through to slow path` ) ;
99133 }
100134 } else {
101- traceVerbose ( `[${ opts . label } ] Fast path: no persisted path, falling through to slow path` ) ;
135+ const scope = opts . scope as Uri ;
136+ const persistedPath = await opts . getPersistedPath ( opts . getProjectFsPath ( scope ) ) ;
137+
138+ if ( persistedPath ) {
139+ try {
140+ const resolved = await opts . resolve ( persistedPath ) ;
141+ if ( resolved ) {
142+ return { env : resolved } ;
143+ }
144+ } catch ( err ) {
145+ traceWarn (
146+ `[${ opts . label } ] Fast path resolve failed for '${ persistedPath } ', falling back to full init:` ,
147+ err ,
148+ ) ;
149+ }
150+ } else {
151+ traceVerbose ( `[${ opts . label } ] Fast path: no persisted path, falling through to slow path` ) ;
152+ }
102153 }
103154
104155 return undefined ;
0 commit comments