Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 4 additions & 11 deletions packages/amazonq/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { Auth, AuthUtils, CredentialsStore, LoginManager, initializeAuth } from 'aws-core-vscode/auth'
import { AuthUtils, CredentialsStore, LoginManager, initializeAuth } from 'aws-core-vscode/auth'
import { activate as activateCodeWhisperer, shutdown as shutdownCodeWhisperer } from 'aws-core-vscode/codewhisperer'
import { makeEndpointsProvider, registerGenericCommands } from 'aws-core-vscode'
import { CommonAuthWebview } from 'aws-core-vscode/login'
Expand Down Expand Up @@ -44,7 +44,6 @@ import * as vscode from 'vscode'
import { registerCommands } from './commands'
import { focusAmazonQPanel } from 'aws-core-vscode/codewhispererChat'
import { activate as activateAmazonqLsp } from './lsp/activation'
import { activate as activateInlineCompletion } from './app/inline/activation'
import { hasGlibcPatch } from './lsp/client'

export const amazonQContextPrefix = 'amazonq'
Expand Down Expand Up @@ -126,17 +125,11 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is

// This contains every lsp agnostic things (auth, security scan, code scan)
await activateCodeWhisperer(extContext as ExtContext)
if (
(Experiments.instance.get('amazonqLSP', true) || Auth.instance.isInternalAmazonUser()) &&
(!isAmazonLinux2() || hasGlibcPatch())
) {
// start the Amazon Q LSP for internal users first
// for AL2, start LSP if glibc patch is found

if (!isAmazonLinux2() || hasGlibcPatch()) {
// Activate Amazon Q LSP for everyone unless they're using AL2 without the glibc patch
await activateAmazonqLsp(context)
}
if (!Experiments.instance.get('amazonqLSPInline', true)) {
await activateInlineCompletion()
}

// Generic extension commands
registerGenericCommands(context, amazonQContextPrefix)
Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/codewhisperer/commands/basicCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,12 @@ const registerToolkitApiCallbackOnce = once(() => {
export const registerToolkitApiCallback = Commands.declare(
{ id: 'aws.amazonq.refreshConnectionCallback' },
() => async (toolkitApi?: any) => {
// Early return if already registered to avoid duplicate work
if (_toolkitApi) {
getLogger().debug('Toolkit API callback already registered, skipping')
return
}

// While the Q/CW exposes an API for the Toolkit to register callbacks on auth changes,
// we need to do it manually here because the Toolkit would have been unable to call
// this API if the Q/CW extension started afterwards (and this code block is running).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export class RegionProfileManager {
result: undefined,
},
},
{ timeout: 15000, interval: 1500, truthy: true }
{ timeout: 15000, interval: 500, truthy: true }
)
}

Expand Down
25 changes: 25 additions & 0 deletions packages/core/src/shared/featureConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ export const featureDefinitions = new Map<FeatureName, FeatureContext>([

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

static #instance: FeatureConfigProvider

Expand Down Expand Up @@ -123,6 +126,28 @@ export class FeatureConfigProvider {
return
}

// Debounce multiple concurrent calls
const now = performance.now()
if (this.fetchPromise && now - this.lastFetchTime < this.minFetchInterval) {
getLogger().debug('amazonq: Debouncing feature config fetch')
return this.fetchPromise
}

if (this.fetchPromise) {
return this.fetchPromise
}

this.lastFetchTime = now
this.fetchPromise = this._fetchFeatureConfigsInternal()

try {
await this.fetchPromise
} finally {
this.fetchPromise = undefined
}
}

private async _fetchFeatureConfigsInternal(): Promise<void> {
getLogger().debug('amazonq: Fetching feature configs')
try {
const response = await this.listFeatureEvaluations()
Expand Down
15 changes: 15 additions & 0 deletions packages/core/src/shared/utilities/resourceCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,21 @@ export abstract class CachedResource<V> {
abstract resourceProvider(): Promise<V>

async getResource(): Promise<V> {
// Check cache without locking first
const quickCheck = this.readCacheOrDefault()
if (quickCheck.resource.result && !quickCheck.resource.locked) {
const duration = now() - quickCheck.resource.timestamp
if (duration < this.expirationInMilli) {
logger.debug(
`cache hit (fast path), duration(%sms) is less than expiration(%sms), returning cached value: %s`,
duration,
this.expirationInMilli,
this.key
)
return quickCheck.resource.result
}
}

const cachedValue = await this.tryLoadResourceAndLock()
const resource = cachedValue?.resource

Expand Down
Loading