From f51d4be887b6c69a7b1e19a07404d8878f758654 Mon Sep 17 00:00:00 2001 From: nicktrn <55853254+nicktrn@users.noreply.github.com> Date: Thu, 24 Apr 2025 10:15:50 +0100 Subject: [PATCH 01/12] parse disconnect description --- .../src/entryPoints/managed/controller.ts | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/cli-v3/src/entryPoints/managed/controller.ts b/packages/cli-v3/src/entryPoints/managed/controller.ts index d6685e8c84..c020636b4a 100644 --- a/packages/cli-v3/src/entryPoints/managed/controller.ts +++ b/packages/cli-v3/src/entryPoints/managed/controller.ts @@ -486,10 +486,32 @@ export class ManagedRunController { }); socket.on("disconnect", (reason, description) => { + const parseDescription = (): + | { + description: string; + context?: string; + } + | undefined => { + if (!description) { + return undefined; + } + + if (description instanceof Error) { + return { + description: description.toString(), + }; + } + + return { + description: description.description, + context: description.context ? String(description.context) : undefined, + }; + }; + this.sendDebugLog({ runId: this.runFriendlyId, message: "Socket disconnected from supervisor", - properties: { reason, description: description?.toString() }, + properties: { reason, ...parseDescription() }, }); }); From a40e6c479a29b7c4cb4c947a9b45f9b3229b7859 Mon Sep 17 00:00:00 2001 From: nicktrn <55853254+nicktrn@users.noreply.github.com> Date: Fri, 25 Apr 2025 15:33:14 +0100 Subject: [PATCH 02/12] decrease snapshot poller logs --- .../src/entryPoints/managed/execution.ts | 7 +- .../cli-v3/src/entryPoints/managed/poller.ts | 94 ++++++++----------- packages/core/src/v3/utils/interval.ts | 4 + 3 files changed, 48 insertions(+), 57 deletions(-) diff --git a/packages/cli-v3/src/entryPoints/managed/execution.ts b/packages/cli-v3/src/entryPoints/managed/execution.ts index 58d41443dc..340896fd6d 100644 --- a/packages/cli-v3/src/entryPoints/managed/execution.ts +++ b/packages/cli-v3/src/entryPoints/managed/execution.ts @@ -240,11 +240,10 @@ export class RunExecution { } if (snapshot.friendlyId === this.currentSnapshotId) { - this.sendDebugLog("handleSnapshotChange: snapshot not changed", snapshotMetadata); - return; + return; } - this.sendDebugLog(`snapshot change: ${snapshot.executionStatus}`, snapshotMetadata); + this.sendDebugLog(`snapshot has changed to: ${snapshot.executionStatus}`, snapshotMetadata); // Reset the snapshot poll interval so we don't do unnecessary work this.snapshotPoller?.resetCurrentInterval(); @@ -897,7 +896,7 @@ export class RunExecution { this.lastHeartbeat = new Date(); } - sendDebugLog( + private sendDebugLog( message: string, properties?: SendDebugLogOptions["properties"], runIdOverride?: string diff --git a/packages/cli-v3/src/entryPoints/managed/poller.ts b/packages/cli-v3/src/entryPoints/managed/poller.ts index 2decd401ee..bcf2e21237 100644 --- a/packages/cli-v3/src/entryPoints/managed/poller.ts +++ b/packages/cli-v3/src/entryPoints/managed/poller.ts @@ -1,5 +1,5 @@ import { WorkloadHttpClient } from "@trigger.dev/core/v3/runEngineWorker"; -import { RunLogger } from "./logger.js"; +import { RunLogger, SendDebugLogOptions } from "./logger.js"; import { IntervalService, RunExecutionData } from "@trigger.dev/core/v3"; export type RunExecutionSnapshotPollerOptions = { @@ -14,89 +14,65 @@ export type RunExecutionSnapshotPollerOptions = { export class RunExecutionSnapshotPoller { private runFriendlyId: string; private snapshotFriendlyId: string; + private enabled: boolean; private readonly httpClient: WorkloadHttpClient; private readonly logger: RunLogger; - private readonly snapshotPollIntervalMs: number; private readonly handleSnapshotChange: (runData: RunExecutionData) => Promise; private readonly poller: IntervalService; constructor(opts: RunExecutionSnapshotPollerOptions) { + this.enabled = false; + this.runFriendlyId = opts.runFriendlyId; this.snapshotFriendlyId = opts.snapshotFriendlyId; this.httpClient = opts.httpClient; this.logger = opts.logger; - this.snapshotPollIntervalMs = opts.snapshotPollIntervalSeconds * 1000; this.handleSnapshotChange = opts.handleSnapshotChange; - this.logger.sendDebugLog({ - runId: this.runFriendlyId, - message: "RunExecutionSnapshotPoller", - properties: { - runFriendlyId: this.runFriendlyId, - snapshotFriendlyId: this.snapshotFriendlyId, - snapshotPollIntervalSeconds: opts.snapshotPollIntervalSeconds, - }, - }); + const intervalMs = opts.snapshotPollIntervalSeconds * 1000; this.poller = new IntervalService({ onInterval: async () => { - if (!this.runFriendlyId) { - this.logger.sendDebugLog({ - runId: this.runFriendlyId, - message: "Skipping snapshot poll, no run ID", - }); - return; - } - - this.logger.sendDebugLog({ - runId: this.runFriendlyId, - message: "Polling for latest snapshot", - }); - - this.logger.sendDebugLog({ - runId: this.runFriendlyId, - message: `snapshot poll: started`, - properties: { - snapshotId: this.snapshotFriendlyId, - }, - }); + this.sendDebugLog("polling for latest snapshot"); const response = await this.httpClient.getRunExecutionData(this.runFriendlyId); if (!response.success) { - this.logger.sendDebugLog({ - runId: this.runFriendlyId, - message: "Snapshot poll failed", - properties: { - error: response.error, - }, - }); - - this.logger.sendDebugLog({ - runId: this.runFriendlyId, - message: `snapshot poll: failed`, - properties: { - snapshotId: this.snapshotFriendlyId, - error: response.error, - }, - }); + this.sendDebugLog("failed to get run execution data", { error: response.error }); + return; + } + if (!this.enabled) { + this.sendDebugLog("poller disabled, skipping snapshot change handler"); return; } await this.handleSnapshotChange(response.data.execution); }, - intervalMs: this.snapshotPollIntervalMs, + intervalMs, leadingEdge: false, onError: async (error) => { - this.logger.sendDebugLog({ - runId: this.runFriendlyId, - message: "Failed to poll for snapshot", - properties: { error: error instanceof Error ? error.message : String(error) }, + this.sendDebugLog("failed to poll for snapshot", { + error: error instanceof Error ? error.message : String(error), }); }, }); + + this.sendDebugLog("created"); + } + + private sendDebugLog(message: string, properties?: SendDebugLogOptions["properties"]) { + this.logger.sendDebugLog({ + runId: this.runFriendlyId, + message: `[poller] ${message}`, + properties: { + ...properties, + runId: this.runFriendlyId, + snapshotId: this.snapshotFriendlyId, + pollIntervalMs: this.poller.intervalMs, + }, + }); } resetCurrentInterval() { @@ -112,10 +88,22 @@ export class RunExecutionSnapshotPoller { } start() { + if (this.enabled) { + this.sendDebugLog("already started"); + return; + } + + this.enabled = true; this.poller.start(); } stop() { + if (!this.enabled) { + this.sendDebugLog("already stopped"); + return; + } + + this.enabled = false; this.poller.stop(); } } diff --git a/packages/core/src/v3/utils/interval.ts b/packages/core/src/v3/utils/interval.ts index 59fd0a94cb..86ec86fc8d 100644 --- a/packages/core/src/v3/utils/interval.ts +++ b/packages/core/src/v3/utils/interval.ts @@ -61,6 +61,10 @@ export class IntervalService { this.resetCurrentInterval(); } + get intervalMs() { + return this._intervalMs; + } + #doInterval = async () => { this.#clearNextInterval(); From 415f4d693e6699cb6fe1dd6df8487b4fdad77a78 Mon Sep 17 00:00:00 2001 From: nicktrn <55853254+nicktrn@users.noreply.github.com> Date: Fri, 25 Apr 2025 15:34:58 +0100 Subject: [PATCH 03/12] remove another useless log --- packages/cli-v3/src/entryPoints/managed/execution.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/cli-v3/src/entryPoints/managed/execution.ts b/packages/cli-v3/src/entryPoints/managed/execution.ts index 340896fd6d..cf9d19b873 100644 --- a/packages/cli-v3/src/entryPoints/managed/execution.ts +++ b/packages/cli-v3/src/entryPoints/managed/execution.ts @@ -191,8 +191,6 @@ export class RunExecution { return; } - this.sendDebugLog(`enqueued snapshot change: ${snapshot.executionStatus}`, snapshotMetadata); - this.snapshotChangeQueue.push(runData); await this.processSnapshotChangeQueue(); } @@ -240,7 +238,7 @@ export class RunExecution { } if (snapshot.friendlyId === this.currentSnapshotId) { - return; + return; } this.sendDebugLog(`snapshot has changed to: ${snapshot.executionStatus}`, snapshotMetadata); From 13370a1f6a13e78866afeb4e3df9e7e094d12eab Mon Sep 17 00:00:00 2001 From: nicktrn <55853254+nicktrn@users.noreply.github.com> Date: Fri, 25 Apr 2025 16:03:49 +0100 Subject: [PATCH 04/12] attempt number change detection --- .../cli-v3/src/entryPoints/managed/execution.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/cli-v3/src/entryPoints/managed/execution.ts b/packages/cli-v3/src/entryPoints/managed/execution.ts index cf9d19b873..2cdda81133 100644 --- a/packages/cli-v3/src/entryPoints/managed/execution.ts +++ b/packages/cli-v3/src/entryPoints/managed/execution.ts @@ -51,6 +51,7 @@ export class RunExecution { private _runFriendlyId?: string; private currentSnapshotId?: string; + private currentAttemptNumber?: number; private currentTaskRunEnv?: Record; private dequeuedAt?: Date; @@ -241,6 +242,12 @@ export class RunExecution { return; } + if (this.currentAttemptNumber && this.currentAttemptNumber !== run.attemptNumber) { + this.sendDebugLog("ERROR: attempt number mismatch", snapshotMetadata); + await this.taskRunProcess?.suspend(); + return; + } + this.sendDebugLog(`snapshot has changed to: ${snapshot.executionStatus}`, snapshotMetadata); // Reset the snapshot poll interval so we don't do unnecessary work @@ -453,6 +460,14 @@ export class RunExecution { // A snapshot was just created, so update the snapshot ID this.currentSnapshotId = start.data.snapshot.friendlyId; + // Also set or update the attempt number - we do this to detect illegal attempt number changes, e.g. from stalled runners coming back online + const attemptNumber = start.data.run.attemptNumber; + if (attemptNumber) { + this.currentAttemptNumber = attemptNumber; + } else { + this.sendDebugLog("ERROR: no attempt number returned from start attempt"); + } + const metrics = this.measureExecutionMetrics({ attemptCreatedAt: attemptStartedAt, dequeuedAt: this.dequeuedAt?.getTime(), From 2f9e59042588bf4b1d2e64e71ad446e6baa79859 Mon Sep 17 00:00:00 2001 From: nicktrn <55853254+nicktrn@users.noreply.github.com> Date: Fri, 25 Apr 2025 17:31:27 +0100 Subject: [PATCH 05/12] ensure no executions or child processes are being reused --- .../src/entryPoints/managed/controller.ts | 15 +++++++-- .../src/entryPoints/managed/execution.ts | 31 ++++++++++++++----- .../cli-v3/src/executions/taskRunProcess.ts | 8 +++++ 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/packages/cli-v3/src/entryPoints/managed/controller.ts b/packages/cli-v3/src/entryPoints/managed/controller.ts index c020636b4a..f73c313d72 100644 --- a/packages/cli-v3/src/entryPoints/managed/controller.ts +++ b/packages/cli-v3/src/entryPoints/managed/controller.ts @@ -178,7 +178,17 @@ export class ManagedRunController { } const execution = async () => { - if (!this.currentExecution || !this.currentExecution.isPreparedForNextRun) { + // If we have an existing execution that isn't prepared for the next run, kill it + if (this.currentExecution && !this.currentExecution.canExecute) { + this.sendDebugLog({ + runId: runFriendlyId, + message: "killing existing execution before starting new run", + }); + await this.currentExecution.kill().catch(() => {}); + this.currentExecution = null; + } + + if (!this.currentExecution || !this.currentExecution.canExecute) { this.currentExecution = new RunExecution({ workerManifest: this.workerManifest, env: this.env, @@ -267,11 +277,12 @@ export class ManagedRunController { if (this.currentExecution?.taskRunEnv) { this.sendDebugLog({ runId: this.runFriendlyId, - message: "waitForNextRun: eagerly recreating task run process", + message: "waitForNextRun: eagerly creating fresh execution for next run", }); const previousTaskRunEnv = this.currentExecution.taskRunEnv; + // Create a fresh execution for the next run this.currentExecution = new RunExecution({ workerManifest: this.workerManifest, env: this.env, diff --git a/packages/cli-v3/src/entryPoints/managed/execution.ts b/packages/cli-v3/src/entryPoints/managed/execution.ts index 2cdda81133..dcdd992529 100644 --- a/packages/cli-v3/src/entryPoints/managed/execution.ts +++ b/packages/cli-v3/src/entryPoints/managed/execution.ts @@ -87,10 +87,6 @@ export class RunExecution { throw new Error("prepareForExecution called after process was already created"); } - if (this.isPreparedForNextRun) { - throw new Error("prepareForExecution called after execution was already prepared"); - } - this.taskRunProcess = this.createTaskRunProcess({ envVars: opts.taskRunEnv, isWarmStart: true, @@ -151,9 +147,14 @@ export class RunExecution { } /** - * Returns true if the execution has been prepared with task run env. + * Returns true if no run has been started yet and the process is prepared for the next run. */ - get isPreparedForNextRun(): boolean { + get canExecute(): boolean { + // If we've ever had a run ID, this execution can't be reused + if (this._runFriendlyId) { + return false; + } + return !!this.taskRunProcess?.isPreparedForNextRun; } @@ -609,8 +610,18 @@ export class RunExecution { metrics: TaskRunExecutionMetrics; isWarmStart?: boolean; }) { + // For immediate retries, we need to ensure the task run process is prepared for the next attempt + if ( + this.runFriendlyId && + this.taskRunProcess && + !this.taskRunProcess.isPreparedForNextAttempt + ) { + this.sendDebugLog("killing existing task run process before executing next attempt"); + await this.kill().catch(() => {}); + } + // To skip this step and eagerly create the task run process, run prepareForExecution first - if (!this.taskRunProcess || !this.isPreparedForNextRun) { + if (!this.taskRunProcess || !this.taskRunProcess.isPreparedForNextRun) { this.taskRunProcess = this.createTaskRunProcess({ envVars, isWarmStart }); } @@ -667,11 +678,15 @@ export class RunExecution { } public exit() { - if (this.isPreparedForNextRun) { + if (this.taskRunProcess?.isPreparedForNextRun) { this.taskRunProcess?.forceExit(); } } + public async kill() { + await this.taskRunProcess?.kill("SIGKILL"); + } + private async complete({ completion }: { completion: TaskRunExecutionResult }): Promise { if (!this.runFriendlyId || !this.currentSnapshotId) { throw new Error("Cannot complete run: missing run or snapshot ID"); diff --git a/packages/cli-v3/src/executions/taskRunProcess.ts b/packages/cli-v3/src/executions/taskRunProcess.ts index cb24dac9f6..ae95ecb109 100644 --- a/packages/cli-v3/src/executions/taskRunProcess.ts +++ b/packages/cli-v3/src/executions/taskRunProcess.ts @@ -89,15 +89,21 @@ export class TaskRunProcess { public onWait: Evt = new Evt(); private _isPreparedForNextRun: boolean = false; + private _isPreparedForNextAttempt: boolean = false; constructor(public readonly options: TaskRunProcessOptions) { this._isPreparedForNextRun = true; + this._isPreparedForNextAttempt = true; } get isPreparedForNextRun() { return this._isPreparedForNextRun; } + get isPreparedForNextAttempt() { + return this._isPreparedForNextAttempt; + } + async cancel() { this._isPreparedForNextRun = false; this._isBeingCancelled = true; @@ -223,6 +229,7 @@ export class TaskRunProcess { isWarmStart?: boolean ): Promise { this._isPreparedForNextRun = false; + this._isPreparedForNextAttempt = false; let resolver: (value: TaskRunExecutionResult) => void; let rejecter: (err?: any) => void; @@ -266,6 +273,7 @@ export class TaskRunProcess { const result = await promise; this._currentExecution = undefined; + this._isPreparedForNextAttempt = true; return result; } From 049d1047c737590e451af14b2e11b002993fdcc4 Mon Sep 17 00:00:00 2001 From: nicktrn <55853254+nicktrn@users.noreply.github.com> Date: Fri, 25 Apr 2025 17:43:44 +0100 Subject: [PATCH 06/12] prevent snapshot change handler from running after execution stopped --- packages/cli-v3/src/entryPoints/managed/execution.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/cli-v3/src/entryPoints/managed/execution.ts b/packages/cli-v3/src/entryPoints/managed/execution.ts index dcdd992529..c63b4c9954 100644 --- a/packages/cli-v3/src/entryPoints/managed/execution.ts +++ b/packages/cli-v3/src/entryPoints/managed/execution.ts @@ -66,6 +66,7 @@ export class RunExecution { private snapshotPoller?: RunExecutionSnapshotPoller; private lastHeartbeat?: Date; + private isShuttingDown = false; constructor(opts: RunExecutionOptions) { this.id = randomBytes(4).toString("hex"); @@ -163,6 +164,11 @@ export class RunExecution { * or when the snapshot poller detects a change */ public async handleSnapshotChange(runData: RunExecutionData): Promise { + if (this.isShuttingDown) { + this.sendDebugLog("handleSnapshotChange: shutting down, skipping"); + return; + } + const { run, snapshot, completedWaitpoints } = runData; const snapshotMetadata = { @@ -985,6 +991,11 @@ export class RunExecution { } private stopServices() { + if (this.isShuttingDown) { + return; + } + + this.isShuttingDown = true; this.snapshotPoller?.stop(); this.taskRunProcess?.onTaskRunHeartbeat.detach(); } From 26120e5c43493bd3bd459a277a97bd36077f75c8 Mon Sep 17 00:00:00 2001 From: nicktrn <55853254+nicktrn@users.noreply.github.com> Date: Sun, 27 Apr 2025 12:14:35 +0100 Subject: [PATCH 07/12] prevent creating zombie intervals on reset --- packages/core/src/v3/utils/interval.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/core/src/v3/utils/interval.ts b/packages/core/src/v3/utils/interval.ts index 86ec86fc8d..9470ae2bb2 100644 --- a/packages/core/src/v3/utils/interval.ts +++ b/packages/core/src/v3/utils/interval.ts @@ -13,6 +13,7 @@ export class IntervalService { private _nextInterval: NodeJS.Timeout | undefined; private _leadingEdge: boolean; private _isEnabled: boolean; + private _isExecuting: boolean; constructor(opts: IntervalServiceOptions) { this._onInterval = opts.onInterval; @@ -22,6 +23,7 @@ export class IntervalService { this._nextInterval = undefined; this._leadingEdge = opts.leadingEdge ?? false; this._isEnabled = false; + this._isExecuting = false; } start() { @@ -52,6 +54,10 @@ export class IntervalService { return; } + if (this._isExecuting) { + return; + } + this.#clearNextInterval(); this.#scheduleNextInterval(); } @@ -72,6 +78,13 @@ export class IntervalService { return; } + if (this._isExecuting) { + console.error("Interval handler already running, skipping"); + return; + } + + this._isExecuting = true; + try { await this._onInterval(); } catch (error) { @@ -82,9 +95,10 @@ export class IntervalService { console.error("Error during interval error handler", error); } } + } finally { + this.#scheduleNextInterval(); + this._isExecuting = false; } - - this.#scheduleNextInterval(); }; #clearNextInterval() { From 308c1298ac2b306ed72db058a6820f19b0d5648d Mon Sep 17 00:00:00 2001 From: nicktrn <55853254+nicktrn@users.noreply.github.com> Date: Mon, 28 Apr 2025 12:02:38 +0100 Subject: [PATCH 08/12] add changeset --- .changeset/tidy-books-smell.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changeset/tidy-books-smell.md diff --git a/.changeset/tidy-books-smell.md b/.changeset/tidy-books-smell.md new file mode 100644 index 0000000000..b8ecf87f55 --- /dev/null +++ b/.changeset/tidy-books-smell.md @@ -0,0 +1,8 @@ +--- +"trigger.dev": patch +"@trigger.dev/core": patch +--- + +- Fix polling interval reset bug that could create duplicate intervals +- Protect against unexpected attempt number changes +- Prevent run execution zombies after warm starts \ No newline at end of file From 06a78993aad71d6c0092d3325d0e878d16e86768 Mon Sep 17 00:00:00 2001 From: nicktrn <55853254+nicktrn@users.noreply.github.com> Date: Mon, 28 Apr 2025 12:10:16 +0100 Subject: [PATCH 09/12] add enabled poller check before getting latest snapshot --- packages/cli-v3/src/entryPoints/managed/poller.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/cli-v3/src/entryPoints/managed/poller.ts b/packages/cli-v3/src/entryPoints/managed/poller.ts index bcf2e21237..814833846e 100644 --- a/packages/cli-v3/src/entryPoints/managed/poller.ts +++ b/packages/cli-v3/src/entryPoints/managed/poller.ts @@ -34,6 +34,11 @@ export class RunExecutionSnapshotPoller { this.poller = new IntervalService({ onInterval: async () => { + if (!this.enabled) { + this.sendDebugLog("poller disabled, skipping snapshot change handler (pre)"); + return; + } + this.sendDebugLog("polling for latest snapshot"); const response = await this.httpClient.getRunExecutionData(this.runFriendlyId); @@ -44,7 +49,7 @@ export class RunExecutionSnapshotPoller { } if (!this.enabled) { - this.sendDebugLog("poller disabled, skipping snapshot change handler"); + this.sendDebugLog("poller disabled, skipping snapshot change handler (post)"); return; } From e25a08e4c2070085e83ab303b1cc99b8744c0ed2 Mon Sep 17 00:00:00 2001 From: nicktrn <55853254+nicktrn@users.noreply.github.com> Date: Mon, 28 Apr 2025 12:17:08 +0100 Subject: [PATCH 10/12] make attempt number update more explicit, improve logs --- packages/cli-v3/src/entryPoints/managed/execution.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/cli-v3/src/entryPoints/managed/execution.ts b/packages/cli-v3/src/entryPoints/managed/execution.ts index c63b4c9954..927ed409fe 100644 --- a/packages/cli-v3/src/entryPoints/managed/execution.ts +++ b/packages/cli-v3/src/entryPoints/managed/execution.ts @@ -469,10 +469,12 @@ export class RunExecution { // Also set or update the attempt number - we do this to detect illegal attempt number changes, e.g. from stalled runners coming back online const attemptNumber = start.data.run.attemptNumber; - if (attemptNumber) { + if (attemptNumber && attemptNumber > 0) { this.currentAttemptNumber = attemptNumber; } else { - this.sendDebugLog("ERROR: no attempt number returned from start attempt"); + this.sendDebugLog("ERROR: invalid attempt number returned from start attempt", { + attemptNumber: String(attemptNumber), + }); } const metrics = this.measureExecutionMetrics({ From 789ef6a391d768eb22137e5b9f5dc160b1b70e14 Mon Sep 17 00:00:00 2001 From: nicktrn <55853254+nicktrn@users.noreply.github.com> Date: Mon, 28 Apr 2025 13:02:53 +0100 Subject: [PATCH 11/12] remove deprecated heartbeat service --- packages/core/src/v3/utils/heartbeat.ts | 96 ------------------------- 1 file changed, 96 deletions(-) delete mode 100644 packages/core/src/v3/utils/heartbeat.ts diff --git a/packages/core/src/v3/utils/heartbeat.ts b/packages/core/src/v3/utils/heartbeat.ts deleted file mode 100644 index c9bb0d97ed..0000000000 --- a/packages/core/src/v3/utils/heartbeat.ts +++ /dev/null @@ -1,96 +0,0 @@ -type HeartbeatServiceOptions = { - heartbeat: () => Promise; - intervalMs?: number; - leadingEdge?: boolean; - onError?: (error: unknown) => Promise; -}; - -/** - * @deprecated Use IntervalService instead - */ -export class HeartbeatService { - private _heartbeat: () => Promise; - private _intervalMs: number; - private _nextHeartbeat: NodeJS.Timeout | undefined; - private _leadingEdge: boolean; - private _isHeartbeating: boolean; - private _onError?: (error: unknown) => Promise; - - constructor(opts: HeartbeatServiceOptions) { - this._heartbeat = opts.heartbeat; - this._intervalMs = opts.intervalMs ?? 45_000; - this._nextHeartbeat = undefined; - this._leadingEdge = opts.leadingEdge ?? false; - this._isHeartbeating = false; - this._onError = opts.onError; - } - - start() { - if (this._isHeartbeating) { - return; - } - - this._isHeartbeating = true; - - if (this._leadingEdge) { - this.#doHeartbeat(); - } else { - this.#scheduleNextHeartbeat(); - } - } - - stop() { - if (!this._isHeartbeating) { - return; - } - - this._isHeartbeating = false; - this.#clearNextHeartbeat(); - } - - resetCurrentInterval() { - if (!this._isHeartbeating) { - return; - } - - this.#clearNextHeartbeat(); - this.#scheduleNextHeartbeat(); - } - - updateInterval(intervalMs: number) { - this._intervalMs = intervalMs; - this.resetCurrentInterval(); - } - - #doHeartbeat = async () => { - this.#clearNextHeartbeat(); - - if (!this._isHeartbeating) { - return; - } - - try { - await this._heartbeat(); - } catch (error) { - if (this._onError) { - try { - await this._onError(error); - } catch (error) { - console.error("Error handling heartbeat error", error); - } - } - } - - this.#scheduleNextHeartbeat(); - }; - - #clearNextHeartbeat() { - if (this._nextHeartbeat) { - clearTimeout(this._nextHeartbeat); - } - } - - #scheduleNextHeartbeat() { - this._nextHeartbeat = setTimeout(this.#doHeartbeat, this._intervalMs); - } -} From aef9ccb15250c6fd2ffc887aa106e85c86ea1cce Mon Sep 17 00:00:00 2001 From: nicktrn <55853254+nicktrn@users.noreply.github.com> Date: Mon, 28 Apr 2025 13:21:27 +0100 Subject: [PATCH 12/12] ..also remove the import --- packages/core/src/v3/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/core/src/v3/index.ts b/packages/core/src/v3/index.ts index 8877393dca..3bd1fc4547 100644 --- a/packages/core/src/v3/index.ts +++ b/packages/core/src/v3/index.ts @@ -68,7 +68,6 @@ export { export * from "./utils/imageRef.js"; export * from "./utils/interval.js"; -export * from "./utils/heartbeat.js"; export * from "./config.js"; export {