Skip to content

Commit 64490a1

Browse files
committed
fix(amazonq): Reduce plugin start-up latency
1 parent 028f4f3 commit 64490a1

File tree

5 files changed

+56
-4
lines changed

5 files changed

+56
-4
lines changed

packages/amazonq/src/extension.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,20 +124,26 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is
124124
// Configure proxy settings early
125125
await ProxyUtil.configureProxyForLanguageServer()
126126

127+
// Parallelize independent activation tasks
127128
// This contains every lsp agnostic things (auth, security scan, code scan)
128-
await activateCodeWhisperer(extContext as ExtContext)
129+
const activationTasks = [activateCodeWhisperer(extContext as ExtContext)]
130+
129131
if (
130132
(Experiments.instance.get('amazonqLSP', true) || Auth.instance.isInternalAmazonUser()) &&
131133
(!isAmazonLinux2() || hasGlibcPatch())
132134
) {
133135
// start the Amazon Q LSP for internal users first
134136
// for AL2, start LSP if glibc patch is found
135-
await activateAmazonqLsp(context)
137+
activationTasks.push(activateAmazonqLsp(context))
136138
}
139+
137140
if (!Experiments.instance.get('amazonqLSPInline', true)) {
138-
await activateInlineCompletion()
141+
activationTasks.push(activateInlineCompletion())
139142
}
140143

144+
// Wait for all activation tasks to complete
145+
await Promise.all(activationTasks)
146+
141147
// Generic extension commands
142148
registerGenericCommands(context, amazonQContextPrefix)
143149

packages/core/src/codewhisperer/commands/basicCommands.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,12 @@ const registerToolkitApiCallbackOnce = once(() => {
640640
export const registerToolkitApiCallback = Commands.declare(
641641
{ id: 'aws.amazonq.refreshConnectionCallback' },
642642
() => async (toolkitApi?: any) => {
643+
// Early return if already registered to avoid duplicate work
644+
if (_toolkitApi) {
645+
getLogger().debug('Toolkit API callback already registered, skipping')
646+
return
647+
}
648+
643649
// While the Q/CW exposes an API for the Toolkit to register callbacks on auth changes,
644650
// we need to do it manually here because the Toolkit would have been unable to call
645651
// this API if the Q/CW extension started afterwards (and this code block is running).

packages/core/src/codewhisperer/region/regionProfileManager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export class RegionProfileManager {
7777
result: undefined,
7878
},
7979
},
80-
{ timeout: 15000, interval: 1500, truthy: true }
80+
{ timeout: 15000, interval: 500, truthy: true }
8181
)
8282
}
8383

packages/core/src/shared/featureConfig.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ export const featureDefinitions = new Map<FeatureName, FeatureContext>([
5555

5656
export class FeatureConfigProvider {
5757
private featureConfigs = new Map<string, FeatureContext>()
58+
private fetchPromise: Promise<void> | undefined = undefined
59+
private lastFetchTime = 0
60+
private readonly minFetchInterval = 5000 // 5 seconds minimum between fetches
5861

5962
static #instance: FeatureConfigProvider
6063

@@ -123,6 +126,28 @@ export class FeatureConfigProvider {
123126
return
124127
}
125128

129+
// Debounce multiple concurrent calls
130+
const now = Date.now()
131+
if (this.fetchPromise && now - this.lastFetchTime < this.minFetchInterval) {
132+
getLogger().debug('amazonq: Debouncing feature config fetch')
133+
return this.fetchPromise
134+
}
135+
136+
if (this.fetchPromise) {
137+
return this.fetchPromise
138+
}
139+
140+
this.lastFetchTime = now
141+
this.fetchPromise = this._fetchFeatureConfigsInternal()
142+
143+
try {
144+
await this.fetchPromise
145+
} finally {
146+
this.fetchPromise = undefined
147+
}
148+
}
149+
150+
private async _fetchFeatureConfigsInternal(): Promise<void> {
126151
getLogger().debug('amazonq: Fetching feature configs')
127152
try {
128153
const response = await this.listFeatureEvaluations()

packages/core/src/shared/utilities/resourceCache.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,21 @@ export abstract class CachedResource<V> {
6060
abstract resourceProvider(): Promise<V>
6161

6262
async getResource(): Promise<V> {
63+
// Check cache without locking first
64+
const quickCheck = this.readCacheOrDefault()
65+
if (quickCheck.resource.result && !quickCheck.resource.locked) {
66+
const duration = now() - quickCheck.resource.timestamp
67+
if (duration < this.expirationInMilli) {
68+
logger.debug(
69+
`cache hit (fast path), duration(%sms) is less than expiration(%sms), returning cached value: %s`,
70+
duration,
71+
this.expirationInMilli,
72+
this.key
73+
)
74+
return quickCheck.resource.result
75+
}
76+
}
77+
6378
const cachedValue = await this.tryLoadResourceAndLock()
6479
const resource = cachedValue?.resource
6580

0 commit comments

Comments
 (0)