Skip to content

Commit 7cdd434

Browse files
committed
ref: Use idleTimout if no activities occur in idle transaction
1 parent 37bde11 commit 7cdd434

File tree

3 files changed

+46
-1
lines changed

3 files changed

+46
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- [react] feat: Export `createReduxEnhancer` to log redux actions as breadcrumbs, and attach state as an extra. (#2717)
77
- [tracing] feat: `Add @sentry/tracing` (#2719)
88
- [gatsby] fix: Make APM optional in gatsby package
9+
- [tracing] ref: Use idleTimout if no activities occur in idle transaction
910

1011
## 5.19.2
1112

packages/tracing/src/idletransaction.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ export class IdleTransaction extends Transaction {
7070

7171
private readonly _beforeFinishCallbacks: BeforeFinishCallback[] = [];
7272

73+
// If a transaction is created and no activities are added, we want to make sure that
74+
// it times out properly. This is cleared and not used when activities are added.
75+
private _initTimeout: any;
76+
7377
public constructor(
7478
transactionContext: TransactionContext,
7579
private readonly _idleHub?: Hub,
@@ -188,6 +192,11 @@ export class IdleTransaction extends Transaction {
188192
* @param spanId The span id that represents the activity
189193
*/
190194
private _pushActivity(spanId: string): void {
195+
if (this._initTimeout) {
196+
// tslint:disable-next-line: no-unsafe-any
197+
clearTimeout(this._initTimeout);
198+
this._initTimeout = undefined;
199+
}
191200
logger.log(`[Tracing] pushActivity: ${spanId}`);
192201
this.activities[spanId] = true;
193202
logger.log('[Tracing] new activities count', Object.keys(this.activities).length);
@@ -235,12 +244,25 @@ export class IdleTransaction extends Transaction {
235244
*/
236245
public initSpanRecorder(maxlen?: number): void {
237246
if (!this.spanRecorder) {
247+
this._initTimeout = setTimeout(() => {
248+
if (!this._finished) {
249+
this.finish();
250+
}
251+
}, this._idleTimeout);
252+
238253
const pushActivity = (id: string) => {
254+
if (this._finished) {
255+
return;
256+
}
239257
this._pushActivity(id);
240258
};
241259
const popActivity = (id: string) => {
260+
if (this._finished) {
261+
return;
262+
}
242263
this._popActivity(id);
243264
};
265+
244266
this.spanRecorder = new IdleTransactionSpanRecorder(pushActivity, popActivity, this.spanId, maxlen);
245267

246268
// Start heartbeat so that transactions do not run forever.

packages/tracing/test/idletransaction.test.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { BrowserClient } from '@sentry/browser';
22
import { Hub } from '@sentry/hub';
33

4-
import { IdleTransaction, IdleTransactionSpanRecorder } from '../src/idletransaction';
4+
import { IdleTransaction, IdleTransactionSpanRecorder, DEFAULT_IDLE_TIMEOUT } from '../src/idletransaction';
55
import { Span } from '../src/span';
66
import { SpanStatus } from '../src/spanstatus';
77

@@ -145,6 +145,25 @@ describe('IdleTransaction', () => {
145145
}
146146
});
147147

148+
describe('_initTimeout', () => {
149+
it('finishes if no activities are added to the transaction', () => {
150+
const transaction = new IdleTransaction({ name: 'foo', startTimestamp: 1234 }, hub, 1000);
151+
transaction.initSpanRecorder(10);
152+
153+
jest.runTimersToTime(DEFAULT_IDLE_TIMEOUT);
154+
expect(transaction.endTimestamp).toBeDefined();
155+
});
156+
157+
it('does not finish if a activity is started', () => {
158+
const transaction = new IdleTransaction({ name: 'foo', startTimestamp: 1234 }, hub, 1000);
159+
transaction.initSpanRecorder(10);
160+
transaction.startChild({});
161+
162+
jest.runTimersToTime(DEFAULT_IDLE_TIMEOUT);
163+
expect(transaction.endTimestamp).toBeUndefined();
164+
});
165+
});
166+
148167
describe('heartbeat', () => {
149168
it('does not start heartbeat if there is no span recorder', () => {
150169
const transaction = new IdleTransaction({ name: 'foo' }, hub, 1000);
@@ -164,12 +183,14 @@ describe('IdleTransaction', () => {
164183
jest.runOnlyPendingTimers();
165184
expect(mockFinish).toHaveBeenCalledTimes(0);
166185
});
186+
167187
it('finishes a transaction after 3 beats', () => {
168188
const transaction = new IdleTransaction({ name: 'foo' }, hub, 1000);
169189
const mockFinish = jest.spyOn(transaction, 'finish');
170190
transaction.initSpanRecorder(10);
171191

172192
expect(mockFinish).toHaveBeenCalledTimes(0);
193+
transaction.startChild({});
173194

174195
// Beat 1
175196
jest.runOnlyPendingTimers();
@@ -190,6 +211,7 @@ describe('IdleTransaction', () => {
190211
transaction.initSpanRecorder(10);
191212

192213
expect(mockFinish).toHaveBeenCalledTimes(0);
214+
transaction.startChild({});
193215

194216
// Beat 1
195217
jest.runOnlyPendingTimers();

0 commit comments

Comments
 (0)