1+ /* eslint-disable max-lines */
12import { Hub } from '@sentry/hub' ;
23import { TransactionContext } from '@sentry/types' ;
3- import { logger , timestampWithMs } from '@sentry/utils' ;
4+ import { getGlobalObject , logger , timestampWithMs } from '@sentry/utils' ;
45
5- import { FINISH_REASON_TAG , IDLE_TRANSACTION_FINISH_REASONS } from './constants' ;
66import { IS_DEBUG_BUILD } from './flags' ;
77import { Span , SpanRecorder } from './span' ;
88import { Transaction } from './transaction' ;
99
1010export const DEFAULT_IDLE_TIMEOUT = 1000 ;
11+ export const DEFAULT_FINAL_TIMEOUT = 30000 ;
1112export const HEARTBEAT_INTERVAL = 5000 ;
1213
14+ const global = getGlobalObject < Window > ( ) ;
15+
1316/**
1417 * @inheritDoc
1518 */
1619export class IdleTransactionSpanRecorder extends SpanRecorder {
1720 public constructor (
1821 private readonly _pushActivity : ( id : string ) => void ,
1922 private readonly _popActivity : ( id : string ) => void ,
20- public transactionSpanId : string = '' ,
23+ public transactionSpanId : string ,
2124 maxlen ?: number ,
2225 ) {
2326 super ( maxlen ) ;
@@ -69,25 +72,27 @@ export class IdleTransaction extends Transaction {
6972 private readonly _beforeFinishCallbacks : BeforeFinishCallback [ ] = [ ] ;
7073
7174 /**
72- * If a transaction is created and no activities are added, we want to make sure that
73- * it times out properly. This is cleared and not used when activities are added.
75+ * Timer that tracks a
7476 */
75- private _initTimeout : ReturnType < typeof setTimeout > | undefined ;
77+ private _idleTimeoutID : ReturnType < typeof global . setTimeout > | undefined ;
7678
7779 public constructor (
7880 transactionContext : TransactionContext ,
79- private readonly _idleHub ? : Hub ,
81+ private readonly _idleHub : Hub ,
8082 /**
8183 * The time to wait in ms until the idle transaction will be finished.
82- * @default 1000
8384 */
8485 private readonly _idleTimeout : number = DEFAULT_IDLE_TIMEOUT ,
86+ /**
87+ * The final value in ms that a transaction cannot exceed
88+ */
89+ private readonly _finalTimeout : number = DEFAULT_FINAL_TIMEOUT ,
8590 // Whether or not the transaction should put itself on the scope when it starts and pop itself off when it ends
8691 private readonly _onScope : boolean = false ,
8792 ) {
8893 super ( transactionContext , _idleHub ) ;
8994
90- if ( _idleHub && _onScope ) {
95+ if ( _onScope ) {
9196 // There should only be one active transaction on the scope
9297 clearActiveTransaction ( _idleHub ) ;
9398
@@ -97,16 +102,19 @@ export class IdleTransaction extends Transaction {
97102 _idleHub . configureScope ( scope => scope . setSpan ( this ) ) ;
98103 }
99104
100- this . _initTimeout = setTimeout ( ( ) => {
105+ this . _startIdleTimeout ( ) ;
106+ global . setTimeout ( ( ) => {
101107 if ( ! this . _finished ) {
108+ this . setStatus ( 'deadline_exceeded' ) ;
102109 this . finish ( ) ;
103110 }
104- } , this . _idleTimeout ) ;
111+ } , this . _finalTimeout ) ;
105112 }
106113
107114 /** {@inheritDoc } */
108115 public finish ( endTimestamp : number = timestampWithMs ( ) ) : string | undefined {
109116 this . _finished = true ;
117+ this . _cancelIdleTimeout ( ) ;
110118 this . activities = { } ;
111119
112120 if ( this . spanRecorder ) {
@@ -193,15 +201,34 @@ export class IdleTransaction extends Transaction {
193201 this . spanRecorder . add ( this ) ;
194202 }
195203
204+ /**
205+ * Cancels the existing idletimeout, if there is one
206+ */
207+ private _cancelIdleTimeout ( ) : void {
208+ if ( this . _idleTimeoutID ) {
209+ global . clearTimeout ( this . _idleTimeoutID ) ;
210+ this . _idleTimeoutID = undefined ;
211+ }
212+ }
213+
214+ /**
215+ * Creates an idletimeout
216+ */
217+ private _startIdleTimeout ( endTimestamp ?: Parameters < IdleTransaction [ 'finish' ] > [ 0 ] ) : void {
218+ this . _cancelIdleTimeout ( ) ;
219+ this . _idleTimeoutID = global . setTimeout ( ( ) => {
220+ if ( ! this . _finished && Object . keys ( this . activities ) . length === 0 ) {
221+ this . finish ( endTimestamp ) ;
222+ }
223+ } , this . _idleTimeout ) ;
224+ }
225+
196226 /**
197227 * Start tracking a specific activity.
198228 * @param spanId The span id that represents the activity
199229 */
200230 private _pushActivity ( spanId : string ) : void {
201- if ( this . _initTimeout ) {
202- clearTimeout ( this . _initTimeout ) ;
203- this . _initTimeout = undefined ;
204- }
231+ this . _cancelIdleTimeout ( ) ;
205232 IS_DEBUG_BUILD && logger . log ( `[Tracing] pushActivity: ${ spanId } ` ) ;
206233 this . activities [ spanId ] = true ;
207234 IS_DEBUG_BUILD && logger . log ( '[Tracing] new activities count' , Object . keys ( this . activities ) . length ) ;
@@ -220,17 +247,10 @@ export class IdleTransaction extends Transaction {
220247 }
221248
222249 if ( Object . keys ( this . activities ) . length === 0 ) {
223- const timeout = this . _idleTimeout ;
224250 // We need to add the timeout here to have the real endtimestamp of the transaction
225251 // Remember timestampWithMs is in seconds, timeout is in ms
226- const end = timestampWithMs ( ) + timeout / 1000 ;
227-
228- setTimeout ( ( ) => {
229- if ( ! this . _finished ) {
230- this . setTag ( FINISH_REASON_TAG , IDLE_TRANSACTION_FINISH_REASONS [ 1 ] ) ;
231- this . finish ( end ) ;
232- }
233- } , timeout ) ;
252+ const endTimestamp = timestampWithMs ( ) + this . _idleTimeout / 1000 ;
253+ this . _startIdleTimeout ( endTimestamp ) ;
234254 }
235255 }
236256
@@ -257,7 +277,6 @@ export class IdleTransaction extends Transaction {
257277 if ( this . _heartbeatCounter >= 3 ) {
258278 IS_DEBUG_BUILD && logger . log ( '[Tracing] Transaction finished because of no change for 3 heart beats' ) ;
259279 this . setStatus ( 'deadline_exceeded' ) ;
260- this . setTag ( FINISH_REASON_TAG , IDLE_TRANSACTION_FINISH_REASONS [ 0 ] ) ;
261280 this . finish ( ) ;
262281 } else {
263282 this . _pingHeartbeat ( ) ;
@@ -269,7 +288,7 @@ export class IdleTransaction extends Transaction {
269288 */
270289 private _pingHeartbeat ( ) : void {
271290 IS_DEBUG_BUILD && logger . log ( `pinging Heartbeat -> current counter: ${ this . _heartbeatCounter } ` ) ;
272- setTimeout ( ( ) => {
291+ global . setTimeout ( ( ) => {
273292 this . _beat ( ) ;
274293 } , HEARTBEAT_INTERVAL ) ;
275294 }
0 commit comments