@@ -14,7 +14,6 @@ import { RunLogger, SendDebugLogOptions } from "./logger.js";
14
14
import { RunnerEnv } from "./env.js" ;
15
15
import { WorkloadHttpClient } from "@trigger.dev/core/v3/workers" ;
16
16
import { setTimeout as sleep } from "timers/promises" ;
17
- import { RunExecutionHeartbeat } from "./heartbeat.js" ;
18
17
import { RunExecutionSnapshotPoller } from "./poller.js" ;
19
18
import { assertExhaustive , tryCatch } from "@trigger.dev/core/utils" ;
20
19
import { MetadataClient } from "./overrides.js" ;
@@ -63,9 +62,10 @@ export class RunExecution {
63
62
private restoreCount : number ;
64
63
65
64
private taskRunProcess ?: TaskRunProcess ;
66
- private runHeartbeat ?: RunExecutionHeartbeat ;
67
65
private snapshotPoller ?: RunExecutionSnapshotPoller ;
68
66
67
+ private lastHeartbeat ?: Date ;
68
+
69
69
constructor ( opts : RunExecutionOptions ) {
70
70
this . id = randomBytes ( 4 ) . toString ( "hex" ) ;
71
71
this . workerManifest = opts . workerManifest ;
@@ -105,11 +105,12 @@ export class RunExecution {
105
105
envVars : Record < string , string > ;
106
106
isWarmStart ?: boolean ;
107
107
} ) {
108
- return new TaskRunProcess ( {
108
+ const taskRunProcess = new TaskRunProcess ( {
109
109
workerManifest : this . workerManifest ,
110
110
env : {
111
111
...envVars ,
112
112
...this . env . gatherProcessEnv ( ) ,
113
+ HEARTBEAT_INTERVAL_MS : String ( this . env . TRIGGER_HEARTBEAT_INTERVAL_SECONDS * 1000 ) ,
113
114
} ,
114
115
serverWorker : {
115
116
id : "managed" ,
@@ -123,6 +124,29 @@ export class RunExecution {
123
124
} ,
124
125
isWarmStart,
125
126
} ) . initialize ( ) ;
127
+
128
+ taskRunProcess . onTaskRunHeartbeat . attach ( async ( runId ) => {
129
+ if ( ! this . runFriendlyId ) {
130
+ this . sendDebugLog ( "onTaskRunHeartbeat: missing run ID" , { heartbeatRunId : runId } ) ;
131
+ return ;
132
+ }
133
+
134
+ if ( runId !== this . runFriendlyId ) {
135
+ this . sendDebugLog ( "onTaskRunHeartbeat: mismatched run ID" , {
136
+ heartbeatRunId : runId ,
137
+ expectedRunId : this . runFriendlyId ,
138
+ } ) ;
139
+ return ;
140
+ }
141
+
142
+ const [ error ] = await tryCatch ( this . onHeartbeat ( ) ) ;
143
+
144
+ if ( error ) {
145
+ this . sendDebugLog ( "onTaskRunHeartbeat: failed" , { error : error . message } ) ;
146
+ }
147
+ } ) ;
148
+
149
+ return taskRunProcess ;
126
150
}
127
151
128
152
/**
@@ -229,7 +253,6 @@ export class RunExecution {
229
253
this . currentSnapshotId = snapshot . friendlyId ;
230
254
231
255
// Update services
232
- this . runHeartbeat ?. updateSnapshotId ( snapshot . friendlyId ) ;
233
256
this . snapshotPoller ?. updateSnapshotId ( snapshot . friendlyId ) ;
234
257
235
258
switch ( snapshot . executionStatus ) {
@@ -450,13 +473,6 @@ export class RunExecution {
450
473
this . podScheduledAt = runOpts . podScheduledAt ;
451
474
452
475
// Create and start services
453
- this . runHeartbeat = new RunExecutionHeartbeat ( {
454
- runFriendlyId : this . runFriendlyId ,
455
- snapshotFriendlyId : this . currentSnapshotId ,
456
- httpClient : this . httpClient ,
457
- logger : this . logger ,
458
- heartbeatIntervalSeconds : this . env . TRIGGER_HEARTBEAT_INTERVAL_SECONDS ,
459
- } ) ;
460
476
this . snapshotPoller = new RunExecutionSnapshotPoller ( {
461
477
runFriendlyId : this . runFriendlyId ,
462
478
snapshotFriendlyId : this . currentSnapshotId ,
@@ -466,7 +482,6 @@ export class RunExecution {
466
482
handleSnapshotChange : this . handleSnapshotChange . bind ( this ) ,
467
483
} ) ;
468
484
469
- this . runHeartbeat . start ( ) ;
470
485
this . snapshotPoller . start ( ) ;
471
486
472
487
const [ startError , start ] = await tryCatch (
@@ -839,9 +854,6 @@ export class RunExecution {
839
854
this . env . override ( overrides ) ;
840
855
841
856
// Update services with new values
842
- if ( overrides . TRIGGER_HEARTBEAT_INTERVAL_SECONDS ) {
843
- this . runHeartbeat ?. updateInterval ( this . env . TRIGGER_HEARTBEAT_INTERVAL_SECONDS * 1000 ) ;
844
- }
845
857
if ( overrides . TRIGGER_SNAPSHOT_POLL_INTERVAL_SECONDS ) {
846
858
this . snapshotPoller ?. updateInterval ( this . env . TRIGGER_SNAPSHOT_POLL_INTERVAL_SECONDS * 1000 ) ;
847
859
}
@@ -857,6 +869,28 @@ export class RunExecution {
857
869
}
858
870
}
859
871
872
+ private async onHeartbeat ( ) {
873
+ if ( ! this . runFriendlyId ) {
874
+ this . sendDebugLog ( "Heartbeat: missing run ID" ) ;
875
+ return ;
876
+ }
877
+
878
+ if ( ! this . currentSnapshotId ) {
879
+ this . sendDebugLog ( "Heartbeat: missing snapshot ID" ) ;
880
+ return ;
881
+ }
882
+
883
+ this . sendDebugLog ( "Heartbeat: started" ) ;
884
+
885
+ const response = await this . httpClient . heartbeatRun ( this . runFriendlyId , this . currentSnapshotId ) ;
886
+
887
+ if ( ! response . success ) {
888
+ this . sendDebugLog ( "Heartbeat: failed" , { error : response . error } ) ;
889
+ }
890
+
891
+ this . lastHeartbeat = new Date ( ) ;
892
+ }
893
+
860
894
sendDebugLog (
861
895
message : string ,
862
896
properties ?: SendDebugLogOptions [ "properties" ] ,
@@ -871,6 +905,7 @@ export class RunExecution {
871
905
snapshotId : this . currentSnapshotId ,
872
906
executionId : this . id ,
873
907
executionRestoreCount : this . restoreCount ,
908
+ lastHeartbeat : this . lastHeartbeat ?. toISOString ( ) ,
874
909
} ,
875
910
} ) ;
876
911
}
@@ -917,7 +952,7 @@ export class RunExecution {
917
952
}
918
953
919
954
private stopServices ( ) {
920
- this . runHeartbeat ?. stop ( ) ;
921
955
this . snapshotPoller ?. stop ( ) ;
956
+ this . taskRunProcess ?. onTaskRunHeartbeat . detach ( ) ;
922
957
}
923
958
}
0 commit comments