Skip to content

Commit 8e21fb0

Browse files
authored
Fix PowerShell terminal suggestion performance by deduplicating concurrent requests (microsoft#259051)
1 parent 1ac9f61 commit 8e21fb0

File tree

1 file changed

+44
-23
lines changed

1 file changed

+44
-23
lines changed

extensions/terminal-suggest/src/terminalSuggestMain.ts

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ type ShellGlobalsCacheEntry = {
4444

4545
type ShellGlobalsCacheEntryWithMeta = ShellGlobalsCacheEntry & { timestamp: number };
4646
const cachedGlobals: Map<string, ShellGlobalsCacheEntryWithMeta> = new Map();
47+
const inflightRequests: Map<string, Promise<ICompletionResource[] | undefined>> = new Map();
4748
let pathExecutableCache: PathExecutableCache;
4849
const CACHE_KEY = 'terminalSuggestGlobalsCacheV2';
4950
let globalStorageUri: vscode.Uri;
@@ -122,30 +123,50 @@ async function fetchAndCacheShellGlobals(
122123
remoteAuthority?: string,
123124
background?: boolean
124125
): Promise<ICompletionResource[] | undefined> {
125-
try {
126-
let execShellType = shellType;
127-
if (shellType === TerminalShellType.GitBash) {
128-
execShellType = TerminalShellType.Bash; // Git Bash is a bash shell
129-
}
130-
const options: ExecOptionsWithStringEncoding = { encoding: 'utf-8', shell: execShellType, windowsHide: true };
131-
const mixedCommands: (string | ICompletionResource)[] | undefined = await getShellSpecificGlobals.get(shellType)?.(options, existingCommands);
132-
const normalizedCommands = mixedCommands?.map(command => typeof command === 'string' ? ({ label: command }) : command);
133-
if (machineId) {
134-
const cacheKey = getCacheKey(machineId, remoteAuthority, shellType);
135-
cachedGlobals.set(cacheKey, {
136-
commands: normalizedCommands,
137-
existingCommands: existingCommands ? Array.from(existingCommands) : undefined,
138-
timestamp: Date.now()
139-
});
140-
await writeGlobalsCache();
141-
}
142-
return normalizedCommands;
143-
} catch (error) {
144-
if (!background) {
145-
console.error('Error fetching builtin commands:', error);
146-
}
147-
return;
126+
const cacheKey = getCacheKey(machineId ?? 'no-machine-id', remoteAuthority, shellType);
127+
128+
// Check if there's already an in-flight request for this cache key
129+
const existingRequest = inflightRequests.get(cacheKey);
130+
if (existingRequest) {
131+
// Wait for the existing request to complete rather than spawning a new process
132+
return existingRequest;
148133
}
134+
135+
// Create a new request and store it in the inflight map
136+
const requestPromise = (async () => {
137+
try {
138+
let execShellType = shellType;
139+
if (shellType === TerminalShellType.GitBash) {
140+
execShellType = TerminalShellType.Bash; // Git Bash is a bash shell
141+
}
142+
const options: ExecOptionsWithStringEncoding = { encoding: 'utf-8', shell: execShellType, windowsHide: true };
143+
const mixedCommands: (string | ICompletionResource)[] | undefined = await getShellSpecificGlobals.get(shellType)?.(options, existingCommands);
144+
const normalizedCommands = mixedCommands?.map(command => typeof command === 'string' ? ({ label: command }) : command);
145+
if (machineId) {
146+
const cacheKey = getCacheKey(machineId, remoteAuthority, shellType);
147+
cachedGlobals.set(cacheKey, {
148+
commands: normalizedCommands,
149+
existingCommands: existingCommands ? Array.from(existingCommands) : undefined,
150+
timestamp: Date.now()
151+
});
152+
await writeGlobalsCache();
153+
}
154+
return normalizedCommands;
155+
} catch (error) {
156+
if (!background) {
157+
console.error('Error fetching builtin commands:', error);
158+
}
159+
return;
160+
} finally {
161+
// Always remove the promise from inflight requests when done
162+
inflightRequests.delete(cacheKey);
163+
}
164+
})();
165+
166+
// Store the promise in the inflight map
167+
inflightRequests.set(cacheKey, requestPromise);
168+
169+
return requestPromise;
149170
}
150171

151172

0 commit comments

Comments
 (0)