Skip to content

Commit d118b85

Browse files
authored
fix: Clear activeTransaction from the scope and always start idle timers (#3215)
1 parent da669ce commit d118b85

File tree

3 files changed

+38
-20
lines changed

3 files changed

+38
-20
lines changed

packages/tracing/src/browser/browsertracing.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ export class BrowserTracing implements Integration {
208208
logger.log(`[Tracing] Will not send ${finalContext.op} transaction because of beforeNavigate.`);
209209
}
210210

211+
logger.log(`[Tracing] Starting ${finalContext.op} transaction on scope`);
212+
211213
const hub = this._getCurrentHub();
212214
const { location } = getGlobalObject() as WindowOrWorkerGlobalScope & { location: Location };
213215

@@ -218,7 +220,6 @@ export class BrowserTracing implements Integration {
218220
true,
219221
{ location }, // for use in the tracesSampler
220222
);
221-
logger.log(`[Tracing] Starting ${finalContext.op} transaction on scope`);
222223
idleTransaction.registerBeforeFinishCallback((transaction, endTimestamp) => {
223224
this._metrics.addPerformanceEntries(transaction);
224225
adjustTransactionDuration(secToMs(maxTransactionDuration), transaction, endTimestamp);

packages/tracing/src/idletransaction.ts

+11-11
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ export class IdleTransaction extends Transaction {
9393
logger.log(`Setting idle transaction on scope. Span ID: ${this.spanId}`);
9494
_idleHub.configureScope(scope => scope.setSpan(this));
9595
}
96+
97+
this._initTimeout = setTimeout(() => {
98+
if (!this._finished) {
99+
this.finish();
100+
}
101+
}, this._idleTimeout);
96102
}
97103

98104
/** {@inheritDoc} */
@@ -130,16 +136,16 @@ export class IdleTransaction extends Transaction {
130136
return keepSpan;
131137
});
132138

133-
// this._onScope is true if the transaction was previously on the scope.
134-
if (this._onScope) {
135-
clearActiveTransaction(this._idleHub);
136-
}
137-
138139
logger.log('[Tracing] flushing IdleTransaction');
139140
} else {
140141
logger.log('[Tracing] No active IdleTransaction');
141142
}
142143

144+
// this._onScope is true if the transaction was previously on the scope.
145+
if (this._onScope) {
146+
clearActiveTransaction(this._idleHub);
147+
}
148+
143149
return super.finish(endTimestamp);
144150
}
145151

@@ -159,12 +165,6 @@ export class IdleTransaction extends Transaction {
159165
*/
160166
public initSpanRecorder(maxlen?: number): void {
161167
if (!this.spanRecorder) {
162-
this._initTimeout = setTimeout(() => {
163-
if (!this._finished) {
164-
this.finish();
165-
}
166-
}, this._idleTimeout);
167-
168168
const pushActivity = (id: string): void => {
169169
if (this._finished) {
170170
return;

packages/tracing/test/idletransaction.test.ts

+25-8
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ describe('IdleTransaction', () => {
3030
});
3131
});
3232

33-
it('removes transaction from scope on finish if onScope is true', () => {
33+
it('removes sampled transaction from scope on finish if onScope is true', () => {
3434
const transaction = new IdleTransaction({ name: 'foo' }, hub, 1000, true);
3535
transaction.initSpanRecorder(10);
3636

@@ -41,6 +41,17 @@ describe('IdleTransaction', () => {
4141
expect(s.getTransaction()).toBe(undefined);
4242
});
4343
});
44+
45+
it('removes unsampled transaction from scope on finish if onScope is true', () => {
46+
const transaction = new IdleTransaction({ name: 'foo', sampled: false }, hub, 1000, true);
47+
48+
transaction.finish();
49+
jest.runAllTimers();
50+
51+
hub.configureScope(s => {
52+
expect(s.getTransaction()).toBe(undefined);
53+
});
54+
});
4455
});
4556

4657
beforeEach(() => {
@@ -150,7 +161,7 @@ describe('IdleTransaction', () => {
150161
const transaction = new IdleTransaction({ name: 'foo', startTimestamp: 1234 }, hub, 1000);
151162
transaction.initSpanRecorder(10);
152163

153-
jest.runTimersToTime(DEFAULT_IDLE_TIMEOUT);
164+
jest.advanceTimersByTime(DEFAULT_IDLE_TIMEOUT);
154165
expect(transaction.endTimestamp).toBeDefined();
155166
});
156167

@@ -159,28 +170,34 @@ describe('IdleTransaction', () => {
159170
transaction.initSpanRecorder(10);
160171
transaction.startChild({});
161172

162-
jest.runTimersToTime(DEFAULT_IDLE_TIMEOUT);
173+
jest.advanceTimersByTime(DEFAULT_IDLE_TIMEOUT);
163174
expect(transaction.endTimestamp).toBeUndefined();
164175
});
165176
});
166177

167178
describe('heartbeat', () => {
168-
it('does not start heartbeat if there is no span recorder', () => {
169-
const transaction = new IdleTransaction({ name: 'foo' }, hub, 1000);
179+
it('does not mark transaction as `DeadlineExceeded` if idle timeout has not been reached', () => {
180+
const HEARTBEAT_INTERVAL = 5000;
181+
// 20s to exceed 3 heartbeats
182+
const transaction = new IdleTransaction({ name: 'foo' }, hub, 20000);
170183
const mockFinish = jest.spyOn(transaction, 'finish');
171184

185+
expect(transaction.status).not.toEqual(SpanStatus.DeadlineExceeded);
172186
expect(mockFinish).toHaveBeenCalledTimes(0);
173187

174188
// Beat 1
175-
jest.runOnlyPendingTimers();
189+
jest.advanceTimersByTime(HEARTBEAT_INTERVAL);
190+
expect(transaction.status).not.toEqual(SpanStatus.DeadlineExceeded);
176191
expect(mockFinish).toHaveBeenCalledTimes(0);
177192

178193
// Beat 2
179-
jest.runOnlyPendingTimers();
194+
jest.advanceTimersByTime(HEARTBEAT_INTERVAL);
195+
expect(transaction.status).not.toEqual(SpanStatus.DeadlineExceeded);
180196
expect(mockFinish).toHaveBeenCalledTimes(0);
181197

182198
// Beat 3
183-
jest.runOnlyPendingTimers();
199+
jest.advanceTimersByTime(HEARTBEAT_INTERVAL);
200+
expect(transaction.status).not.toEqual(SpanStatus.DeadlineExceeded);
184201
expect(mockFinish).toHaveBeenCalledTimes(0);
185202
});
186203

0 commit comments

Comments
 (0)