Skip to content

Commit 7fec270

Browse files
committed
feat: promise queue to send telemetry data
1 parent b25f999 commit 7fec270

File tree

2 files changed

+121
-41
lines changed

2 files changed

+121
-41
lines changed

src/copilot/utils.ts

Lines changed: 96 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,31 @@ import {
88
type ContextProvider,
99
} from '@github/copilot-language-server';
1010
import { sendInfo } from "vscode-extension-telemetry-wrapper";
11+
12+
/**
13+
* TelemetryQueue - Asynchronous telemetry queue to avoid blocking main thread
14+
* Based on the PromiseQueue pattern from copilot-client
15+
*/
16+
class TelemetryQueue {
17+
private promises = new Set<Promise<unknown>>();
18+
19+
register(promise: Promise<unknown>): void {
20+
this.promises.add(promise);
21+
// Use void to avoid blocking - the key pattern from PromiseQueue
22+
void promise.finally(() => this.promises.delete(promise));
23+
}
24+
25+
async flush(): Promise<void> {
26+
await Promise.allSettled(this.promises);
27+
}
28+
29+
get size(): number {
30+
return this.promises.size;
31+
}
32+
}
33+
34+
// Global telemetry queue instance
35+
const globalTelemetryQueue = new TelemetryQueue();
1136
/**
1237
* Error classes for Copilot context provider cancellation handling
1338
*/
@@ -211,14 +236,61 @@ export class ContextProviderResolverError extends Error {
211236
}
212237

213238
/**
214-
* Send consolidated telemetry data for Java context resolution
215-
* This is the centralized function for sending context resolution telemetry
239+
* Asynchronously send telemetry data preparation and sending
240+
* This function prepares telemetry data and handles the actual sending asynchronously
241+
*/
242+
async function _sendContextResolutionTelemetry(
243+
request: ResolveRequest,
244+
start: number,
245+
items: SupportedContextItem[],
246+
status: string,
247+
error?: string,
248+
dependenciesEmptyReason?: string,
249+
importsEmptyReason?: string,
250+
dependenciesCount?: number,
251+
importsCount?: number
252+
): Promise<void> {
253+
try {
254+
const duration = Math.round(performance.now() - start);
255+
const tokenCount = JavaContextProviderUtils.calculateTokenCount(items);
256+
const telemetryData: any = {
257+
"action": "resolveJavaContext",
258+
"completionId": request.completionId,
259+
"duration": duration,
260+
"itemCount": items.length,
261+
"tokenCount": tokenCount,
262+
"status": status,
263+
"dependenciesCount": dependenciesCount ?? 0,
264+
"importsCount": importsCount ?? 0
265+
};
266+
267+
// Add empty reasons if present
268+
if (dependenciesEmptyReason) {
269+
telemetryData.dependenciesEmptyReason = dependenciesEmptyReason;
270+
}
271+
if (importsEmptyReason) {
272+
telemetryData.importsEmptyReason = importsEmptyReason;
273+
}
274+
if (error) {
275+
telemetryData.error = error;
276+
}
277+
278+
// Actual telemetry sending - this is synchronous but network is async
279+
sendInfo("", telemetryData);
280+
} catch (telemetryError) {
281+
// Silently ignore telemetry errors to not affect main functionality
282+
console.error('Failed to send Java context resolution telemetry:', telemetryError);
283+
}
284+
}
285+
286+
/**
287+
* Send consolidated telemetry data for Java context resolution asynchronously
288+
* This function immediately returns and sends telemetry in the background without blocking
216289
*
217290
* @param request The resolve request from Copilot
218291
* @param start Performance timestamp when resolution started
219292
* @param items The resolved context items
220293
* @param status Status of the resolution ("succeeded", "cancelled_by_copilot", "cancelled_internally", "error_partial_results")
221-
* @param sendInfo The sendInfo function from vscode-extension-telemetry-wrapper
222294
* @param error Optional error message
223295
* @param dependenciesEmptyReason Optional reason why dependencies were empty
224296
* @param importsEmptyReason Optional reason why imports were empty
@@ -236,29 +308,26 @@ export function sendContextResolutionTelemetry(
236308
dependenciesCount?: number,
237309
importsCount?: number
238310
): void {
239-
const duration = Math.round(performance.now() - start);
240-
const tokenCount = JavaContextProviderUtils.calculateTokenCount(items);
241-
const telemetryData: any = {
242-
"action": "resolveJavaContext",
243-
"completionId": request.completionId,
244-
"duration": duration,
245-
"itemCount": items.length,
246-
"tokenCount": tokenCount,
247-
"status": status,
248-
"dependenciesCount": dependenciesCount ?? 0,
249-
"importsCount": importsCount ?? 0
250-
};
251-
252-
// Add empty reasons if present
253-
if (dependenciesEmptyReason) {
254-
telemetryData.dependenciesEmptyReason = dependenciesEmptyReason;
255-
}
256-
if (importsEmptyReason) {
257-
telemetryData.importsEmptyReason = importsEmptyReason;
258-
}
259-
if (error) {
260-
telemetryData.error = error;
261-
}
311+
// Register the telemetry promise for non-blocking execution
312+
// This follows the PromiseQueue pattern from copilot-client
313+
globalTelemetryQueue.register(
314+
_sendContextResolutionTelemetry(
315+
request,
316+
start,
317+
items,
318+
status,
319+
error,
320+
dependenciesEmptyReason,
321+
importsEmptyReason,
322+
dependenciesCount,
323+
importsCount
324+
)
325+
);
326+
}
262327

263-
sendInfo("", telemetryData);
328+
/**
329+
* Get the global telemetry queue instance (useful for testing and monitoring)
330+
*/
331+
export function getTelemetryQueue(): TelemetryQueue {
332+
return globalTelemetryQueue;
264333
}

test/simple/.project

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,28 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<projectDescription>
3-
<name>1.helloworld</name>
4-
<comment></comment>
5-
<projects>
6-
</projects>
7-
<buildSpec>
8-
<buildCommand>
9-
<name>org.eclipse.jdt.core.javabuilder</name>
10-
<arguments>
11-
</arguments>
12-
</buildCommand>
13-
</buildSpec>
14-
<natures>
15-
<nature>org.eclipse.jdt.core.javanature</nature>
16-
</natures>
3+
<name>1.helloworld</name>
4+
<comment></comment>
5+
<projects>
6+
</projects>
7+
<buildSpec>
8+
<buildCommand>
9+
<name>org.eclipse.jdt.core.javabuilder</name>
10+
<arguments>
11+
</arguments>
12+
</buildCommand>
13+
</buildSpec>
14+
<natures>
15+
<nature>org.eclipse.jdt.core.javanature</nature>
16+
</natures>
17+
<filteredResources>
18+
<filter>
19+
<id>1763367965791</id>
20+
<name></name>
21+
<type>30</type>
22+
<matcher>
23+
<id>org.eclipse.core.resources.regexFilterMatcher</id>
24+
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
25+
</matcher>
26+
</filter>
27+
</filteredResources>
1728
</projectDescription>

0 commit comments

Comments
 (0)