Skip to content

Commit 2a294c3

Browse files
committed
more idle transaction refactor
1 parent 1eea4ab commit 2a294c3

File tree

5 files changed

+74
-19
lines changed

5 files changed

+74
-19
lines changed

packages/tracing/src/idletransaction.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ export class IdleTransaction extends Transaction {
5656

5757
private _heartbeatCounter: number = 0;
5858

59+
// We use heartbeat if we finished a transaction
60+
private _finished: boolean = false;
61+
5962
private _finishCallback: Function | undefined = undefined;
6063

6164
private readonly _idleHub: Hub | undefined;
@@ -81,6 +84,10 @@ export class IdleTransaction extends Transaction {
8184
*/
8285
private _beat(): void {
8386
clearTimeout(this._heartbeatTimer);
87+
// We should not be running heartbeat if the idle transaction is finished.
88+
if (this._finished) {
89+
return;
90+
}
8491
const keys = Object.keys(this.activities);
8592
const heartbeatString = keys.length ? keys.reduce((prev: string, current: string) => prev + current) : '';
8693

@@ -122,7 +129,8 @@ export class IdleTransaction extends Transaction {
122129
if (this._idleHub) {
123130
const scope = this._idleHub.getScope();
124131
if (scope) {
125-
if (scope.getSpan() === this) {
132+
const span = scope.getSpan();
133+
if (span && span.spanId === this.spanId) {
126134
scope.setSpan(undefined);
127135
}
128136
}
@@ -163,6 +171,7 @@ export class IdleTransaction extends Transaction {
163171

164172
logger.log('[Tracing] flushing IdleTransaction');
165173
this.finish();
174+
this._finished = true;
166175
this._resetActiveTransaction();
167176
} else {
168177
logger.log('[Tracing] No active IdleTransaction');
@@ -200,7 +209,8 @@ export class IdleTransaction extends Transaction {
200209
}
201210

202211
/**
203-
* Register a callback function that gets excecuted before the transaction finishes
212+
* Register a callback function that gets excecuted before the transaction finishes.
213+
* Useful for cleanup or if you want to add any additional spans based on current context.
204214
*/
205215
public beforeFinish(callback: (transactionSpan: IdleTransaction) => void): void {
206216
this._finishCallback = callback;

packages/tracing/src/integrations/browsertracing.ts

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,14 @@
11
import { Hub } from '@sentry/hub';
2-
import { Event, EventProcessor, Integration, Severity, TransactionContext } from '@sentry/types';
2+
import { Event, EventProcessor, Integration, Severity } from '@sentry/types';
33
import { logger, safeJoin } from '@sentry/utils';
44

5+
import { IdleTransaction } from '../idletransaction';
56
import { SpanStatus } from '../spanstatus';
6-
import { Transaction } from '../transaction';
7-
8-
import {
9-
RoutingInstrumentationClass,
10-
RoutingInstrumentation,
11-
TracingRouterOptions,
12-
TracingRouter,
13-
} from './tracing/router';
7+
8+
import { RoutingInstrumentationClass, TracingRouter, TracingRouterOptions } from './tracing/router';
149
import { Location as LocationType } from './tracing/types';
1510

1611
/**
17-
* TODO: Figure out Tracing._resetActiveTransaction()
18-
* - No clue :/
19-
* - Maybe on finish, all transactions should remove themselves off the scope?
2012
* TODO: Figure out Tracing.finishIdleTransaction()
2113
* - Need beforeFinish() transaction hook here
2214
* TODO: Figure out both XHR and Fetch tracing
@@ -148,7 +140,11 @@ export class BrowserTracing implements Integration {
148140
startTransactionOnPageLoad,
149141
});
150142

151-
routerTracing.init(hub, idleTimeout);
143+
const beforeFinish = (transactionSpan: IdleTransaction): void => {
144+
// BrowserTracing._beforeFinish(transactionSpan);
145+
};
146+
147+
routerTracing.init(hub, idleTimeout, beforeFinish);
152148

153149
// This EventProcessor makes sure that the transaction is not longer than maxTransactionDuration
154150
addGlobalEventProcessor((event: Event) => {
@@ -185,6 +181,15 @@ export class BrowserTracing implements Integration {
185181
});
186182
}
187183

184+
/**
185+
* Called before the idle transaction finishes
186+
* {@see IdleTransaction.beforeFinish}
187+
*/
188+
// private static _beforeFinish(transaction: IdleTransaction): void {
189+
// this._checkIfOutdated;
190+
// //this._addPerformanceEntries(transaction);
191+
// }
192+
188193
/**
189194
* Uses logger.log to log things in the SDK or as breadcrumbs if defined in options
190195
*/

packages/tracing/src/integrations/tracing/backgroundtab.ts

Whitespace-only changes.

packages/tracing/src/integrations/tracing/router.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ export interface RoutingInstrumentation {
5555
* init the routing instrumentation
5656
*/
5757
init(hub: Hub, idleTimeout: number, beforeFinish?: (transactionSpan: IdleTransaction) => void): void;
58+
59+
/**
60+
* Start an idle transaction. Called by init().
61+
*/
62+
startIdleTransaction(hub: Hub, op: 'pageload' | 'navigation', idleTimeout: number): IdleTransaction | undefined;
5863
}
5964

6065
export type RoutingInstrumentationClass = new (_options?: TracingRouterOptions) => RoutingInstrumentation;
@@ -72,8 +77,14 @@ export class TracingRouter implements RoutingInstrumentation {
7277
}
7378
}
7479

75-
/** JSDOC */
76-
private _startIdleTransaction(hub: Hub, op: string, idleTimeout: number): IdleTransaction | undefined {
80+
/**
81+
* Starts an idle transaction.
82+
*/
83+
public startIdleTransaction(
84+
hub: Hub,
85+
op: 'pageload' | 'navigation',
86+
idleTimeout: number,
87+
): IdleTransaction | undefined {
7788
if (!global || !global.location || !hub) {
7889
return undefined;
7990
}
@@ -107,7 +118,7 @@ export class TracingRouter implements RoutingInstrumentation {
107118
*/
108119
public init(hub: Hub, idleTimeout: number, beforeFinish?: (transactionSpan: IdleTransaction) => void): void {
109120
if (this.options.startTransactionOnPageLoad) {
110-
this._activeTransaction = this._startIdleTransaction(hub, 'pageload', idleTimeout);
121+
this._activeTransaction = this.startIdleTransaction(hub, 'pageload', idleTimeout);
111122
if (this._activeTransaction && beforeFinish) {
112123
this._activeTransaction.beforeFinish(beforeFinish);
113124
}
@@ -119,7 +130,7 @@ export class TracingRouter implements RoutingInstrumentation {
119130
if (this._activeTransaction) {
120131
this._activeTransaction.finish(timestampWithMs());
121132
}
122-
this._activeTransaction = this._startIdleTransaction(hub, 'navigation', idleTimeout);
133+
this._activeTransaction = this.startIdleTransaction(hub, 'navigation', idleTimeout);
123134
if (this._activeTransaction && beforeFinish) {
124135
this._activeTransaction.beforeFinish(beforeFinish);
125136
}

packages/tracing/test/idletransaction.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,27 @@ describe('IdleTransaction', () => {
1111
hub = new Hub(new BrowserClient({ tracesSampleRate: 1 }));
1212
});
1313

14+
it('sets the transaction on the scope on creation', () => {
15+
const transaction = new IdleTransaction({ name: 'foo' }, hub, 1000);
16+
transaction.initSpanRecorder(10);
17+
18+
hub.configureScope(s => {
19+
expect(s.getTransaction()).toBe(transaction);
20+
});
21+
});
22+
23+
it('removes transaction from scope on finish', () => {
24+
const transaction = new IdleTransaction({ name: 'foo' }, hub, 1000);
25+
transaction.initSpanRecorder(10);
26+
27+
transaction.finish();
28+
jest.runAllTimers();
29+
30+
hub.configureScope(s => {
31+
expect(s.getTransaction()).toBe(undefined);
32+
});
33+
});
34+
1435
it('push and pops activities', () => {
1536
const mockFinish = jest.fn();
1637
const transaction = new IdleTransaction({ name: 'foo' }, hub, 1000);
@@ -25,8 +46,16 @@ describe('IdleTransaction', () => {
2546

2647
span.finish();
2748
expect(transaction.activities).toMatchObject({});
49+
2850
jest.runOnlyPendingTimers();
51+
expect(mockFinish).toHaveBeenCalledTimes(1);
2952

53+
// After we run all timers, we still want to make sure that
54+
// transaction.finish() is only called once. This tells us that the
55+
// heartbeat was stopped and that the `_finished` class
56+
// property was used properly. This allows us to know if the
57+
// heartbeat was correctly shut down.
58+
jest.runAllTimers();
3059
expect(mockFinish).toHaveBeenCalledTimes(1);
3160
});
3261

0 commit comments

Comments
 (0)