-
Notifications
You must be signed in to change notification settings - Fork 129
fix: don't wait for telemetry events MCP-179 #521
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
4cc2a36
724add9
30194a5
a14e534
dd80fc1
208a419
e9c3c7a
544cd62
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -7,16 +7,29 @@ import { MACHINE_METADATA } from "./constants.js"; | |||||
import { EventCache } from "./eventCache.js"; | ||||||
import { detectContainerEnv } from "../helpers/container.js"; | ||||||
import type { DeviceId } from "../helpers/deviceId.js"; | ||||||
import { EventEmitter } from "stream"; | ||||||
|
||||||
type EventResult = { | ||||||
success: boolean; | ||||||
error?: Error; | ||||||
}; | ||||||
|
||||||
async function timeout(promise: Promise<unknown>, ms: number): Promise<void> { | ||||||
await Promise.race([new Promise((resolve) => setTimeout(resolve, ms)), promise]); | ||||||
} | ||||||
|
||||||
export interface TelemetryEvents { | ||||||
"events-emitted": []; | ||||||
"events-send-failed": []; | ||||||
"events-skipped": []; | ||||||
} | ||||||
|
||||||
export class Telemetry { | ||||||
private isBufferingEvents: boolean = true; | ||||||
/** Resolves when the setup is complete or a timeout occurs */ | ||||||
public setupPromise: Promise<[string, boolean]> | undefined; | ||||||
public readonly events: EventEmitter<TelemetryEvents> = new EventEmitter(); | ||||||
|
||||||
private eventCache: EventCache; | ||||||
private deviceId: DeviceId; | ||||||
|
||||||
|
@@ -57,6 +70,12 @@ export class Telemetry { | |||||
|
||||||
private async setup(): Promise<void> { | ||||||
if (!this.isTelemetryEnabled()) { | ||||||
this.session.logger.info({ | ||||||
id: LogId.telemetryEmitFailure, | ||||||
context: "telemetry", | ||||||
message: "Telemetry is disabled.", | ||||||
noRedaction: true, | ||||||
}); | ||||||
return; | ||||||
} | ||||||
|
||||||
|
@@ -71,34 +90,22 @@ export class Telemetry { | |||||
|
||||||
public async close(): Promise<void> { | ||||||
this.isBufferingEvents = false; | ||||||
await this.emitEvents(this.eventCache.getEvents()); | ||||||
await timeout(this.emit([]), 5_000); | ||||||
|
await timeout(this.emit([]), 5_000); | |
await timeout(this.emit(this.eventCache.getEvents()), 5_000); |
Copilot uses AI. Check for mistakes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is wrong. We're already emitting the cached events in emit
, so passing this.eventCache.getEvents()
would have made it so they're emitted twice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
timeout
function doesn't actually enforce a timeout - it just waits for whichever promise resolves first. Consider usingPromise.race
with a rejection timeout to properly handle cases where the promise takes longer than the specified milliseconds.Copilot uses AI. Check for mistakes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This suggestion is actually good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really like it. The reason why I chose to resolve the promise is because I don't think
close()
should throw if we fail to emit the events on time. I'll add a comment explaining that and remove the function as it's just a 1-liner anyway.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem with this approach is that the timeout is only relevant for waiting, but the actual operation runs in the background (I don't see any usage of an AbortController here). At least the error would show in our logs even if we don't cancel the long-running op.
If you want to keep the resolution as is, I would at least print a log, so timeouts are not invisible. One of the reasons we are implementing this is because we think that telemetry is taking too much time at the end.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you check that this timeout isn't going to lead to the process being held up?
adding
.unref()
https://nodejs.org/api/timers.html#timeoutunref just to be safe would be good to.