Skip to content

Commit d8bb720

Browse files
Autocomplete: Don't delay completions for cached entries (#3138)
Right now, about 15% of all suggested completions originate from our cache (actually quite nice considering we are aggressively invalidating the cache e.g. when rejecting completions). However, I noticed that these have a P75 duration of ~125ms which is caused by the debounce time + retrieval time. We can check the cache before we do any of this work though to remove the latency of cached entries completely. ## Test plan - Updated the unit tests - Stepped into it with a debugger to see the right breakpoints are triggered --------- Co-authored-by: Valery Bugakov <skymk1@gmail.com>
1 parent 1339ef1 commit d8bb720

File tree

4 files changed

+57
-32
lines changed

4 files changed

+57
-32
lines changed

vscode/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ This is a log of all notable changes to Cody for VS Code. [Unreleased] changes a
1212

1313
### Changed
1414

15+
- Autocomplete: Removes the latency for cached completions. [https://github.com/sourcegraph/cody/pull/3138](https://github.com/sourcegraph/cody/pull/3138)
1516
- Autocomplete: Enable the recent jaccard similarity improvements by default. [pull/3135](https://github.com/sourcegraph/cody/pull/3135)
1617

1718
## [1.4.3]

vscode/src/completions/get-inline-completions.ts

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,30 @@ async function doGetInlineCompletions(
282282
traceId: getActiveTraceAndSpanId()?.traceId,
283283
})
284284

285+
const requestParams: RequestParams = {
286+
document,
287+
docContext,
288+
position,
289+
selectedCompletionInfo,
290+
abortSignal,
291+
}
292+
293+
const cachedResult = requestManager.checkCache({
294+
requestParams,
295+
isCacheEnabled: triggerKind !== TriggerKind.Manual,
296+
})
297+
if (cachedResult) {
298+
const { completions, source } = cachedResult
299+
300+
CompletionLogger.loaded(logId, requestParams, completions, source, isDotComUser)
301+
302+
return {
303+
logId,
304+
items: completions,
305+
source,
306+
}
307+
}
308+
285309
// Debounce to avoid firing off too many network requests as the user is still typing.
286310
await wrapInActiveSpan('autocomplete.debounce', async () => {
287311
const interval =
@@ -334,14 +358,6 @@ async function doGetInlineCompletions(
334358

335359
CompletionLogger.networkRequestStarted(logId, contextResult?.logSummary)
336360

337-
const requestParams: RequestParams = {
338-
document,
339-
docContext,
340-
position,
341-
selectedCompletionInfo,
342-
abortSignal,
343-
}
344-
345361
// Get the processed completions from providers
346362
const { completions, source } = await requestManager.request({
347363
requestParams,

vscode/src/completions/request-manager.test.ts

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ describe('RequestManager', () => {
9999
provider: Provider,
100100
suffix?: string
101101
) => Promise<RequestManagerResult>
102+
let checkCache: (prefix: string, suffix?: string) => RequestManagerResult | null
102103
beforeEach(async () => {
103104
await initCompletionProviderConfig({})
104105
const requestManager = new RequestManager()
@@ -110,6 +111,8 @@ describe('RequestManager', () => {
110111
context: [],
111112
isCacheEnabled: true,
112113
})
114+
checkCache = (prefix: string, suffix?: string) =>
115+
requestManager.checkCache({ requestParams: docState(prefix, suffix), isCacheEnabled: true })
113116
})
114117

115118
it('resolves a single request', async () => {
@@ -124,20 +127,6 @@ describe('RequestManager', () => {
124127
expect(source).toBe(InlineCompletionsResultSource.Network)
125128
})
126129

127-
it('resolves a single request', async () => {
128-
const prefix = 'console.log('
129-
const provider1 = createProvider(prefix)
130-
setTimeout(() => provider1.yield(["'hello')"]), 0)
131-
await createRequest(prefix, provider1)
132-
133-
const provider2 = createProvider(prefix)
134-
135-
const { completions, source } = await createRequest(prefix, provider2)
136-
137-
expect(source).toBe(InlineCompletionsResultSource.Cache)
138-
expect(completions[0].insertText).toBe("'hello')")
139-
})
140-
141130
it('does not resolve from cache if the suffix has changed', async () => {
142131
const prefix = 'console.log('
143132
const suffix1 = ')\nconsole.log(1)'
@@ -205,6 +194,20 @@ describe('RequestManager', () => {
205194
provider2.yield(["'world')"])
206195
})
207196

197+
describe('cache', () => {
198+
it('resolves a single request with a cached value without waiting for the debounce timeout', async () => {
199+
const prefix = 'console.log('
200+
const provider1 = createProvider(prefix)
201+
setTimeout(() => provider1.yield(["'hello')"]), 0)
202+
await createRequest(prefix, provider1)
203+
204+
const { completions, source } = checkCache(prefix)!
205+
206+
expect(source).toBe(InlineCompletionsResultSource.Cache)
207+
expect(completions[0].insertText).toBe("'hello')")
208+
})
209+
})
210+
208211
describe('abort logic', () => {
209212
it('aborts a newer request if a prior request resolves it', async () => {
210213
const prefix1 = 'console.'

vscode/src/completions/request-manager.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -73,23 +73,28 @@ export class RequestManager {
7373
// the relevance of existing requests (i.e to find out if the generations are still relevant)
7474
private latestRequestParams: null | RequestsManagerParams = null
7575

76+
public checkCache(
77+
params: Pick<RequestsManagerParams, 'requestParams' | 'isCacheEnabled'>
78+
): RequestManagerResult | null {
79+
const { requestParams, isCacheEnabled } = params
80+
const cachedCompletions = this.cache.get(requestParams)
81+
82+
if (isCacheEnabled && cachedCompletions) {
83+
addAutocompleteDebugEvent('RequestManager.checkCache', { cachedCompletions })
84+
return cachedCompletions
85+
}
86+
return null
87+
}
88+
7689
public async request(params: RequestsManagerParams): Promise<RequestManagerResult> {
7790
const eagerCancellation = completionProviderConfig.getPrefetchedFlag(
7891
FeatureFlag.CodyAutocompleteEagerCancellation
7992
)
8093
this.latestRequestParams = params
8194

82-
const { requestParams, provider, context, isCacheEnabled, tracer } = params
95+
const { requestParams, provider, context, tracer } = params
8396

84-
const cachedCompletions = this.cache.get(requestParams)
85-
addAutocompleteDebugEvent('RequestManager.request', {
86-
cachedCompletions,
87-
isCacheEnabled,
88-
})
89-
90-
if (isCacheEnabled && cachedCompletions) {
91-
return cachedCompletions
92-
}
97+
addAutocompleteDebugEvent('RequestManager.request')
9398

9499
// When request recycling is enabled, we do not pass the original abort signal forward as to
95100
// not interrupt requests that are no longer relevant. Instead, we let all previous requests

0 commit comments

Comments
 (0)