Skip to content

Commit 2e97b79

Browse files
committed
debug(replay): Add debugging statements around event buffer
This is not intended to be merged to prod, I would like to debug how/why we keep hitting EventBuffer limits on Sentry SaaS. Aside from debugging statements, there should be no other behavioral changes.
1 parent 208a8f2 commit 2e97b79

File tree

4 files changed

+80
-24
lines changed

4 files changed

+80
-24
lines changed

packages/replay-internal/src/eventBuffer/EventBufferCompressionWorker.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ export class EventBufferCompressionWorker implements EventBuffer {
6767
this._totalSize += data.length;
6868

6969
if (this._totalSize > REPLAY_MAX_EVENT_BUFFER_SIZE) {
70+
DEBUG_BUILD &&
71+
logger.info(
72+
`Cannot add new event with raw size of ${data.length} to existing buffer of size ${
73+
this._totalSize - data.length
74+
}`,
75+
);
7076
return Promise.reject(new EventBufferSizeExceededError());
7177
}
7278

@@ -82,14 +88,20 @@ export class EventBufferCompressionWorker implements EventBuffer {
8288

8389
/** @inheritdoc */
8490
public clear(): void {
91+
DEBUG_BUILD && logger.info(`Clearing event buffer (${this._totalSize})`);
8592
this._earliestTimestamp = null;
8693
this._totalSize = 0;
8794
this.hasCheckout = false;
8895

8996
// We do not wait on this, as we assume the order of messages is consistent for the worker
90-
this._worker.postMessage('clear').then(null, e => {
91-
DEBUG_BUILD && logger.exception(e, 'Sending "clear" message to worker failed', e);
92-
});
97+
this._worker.postMessage('clear').then(
98+
() => {
99+
DEBUG_BUILD && logger.info('Event buffer cleared within worker');
100+
},
101+
e => {
102+
DEBUG_BUILD && logger.exception(e, 'Sending "clear" message to worker failed', e);
103+
},
104+
);
93105
}
94106

95107
/** @inheritdoc */

packages/replay-internal/src/eventBuffer/EventBufferProxy.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ export class EventBufferProxy implements EventBuffer {
115115
// Wait for original events to be re-added before resolving
116116
try {
117117
await Promise.all(addEventPromises);
118+
DEBUG_BUILD && logger.info('Finished switching to compression worker');
118119

119120
// Can now clear fallback buffer as it's no longer necessary
120121
this._fallback.clear();

packages/replay-internal/src/replay.ts

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
/* eslint-disable max-lines */ // TODO: We might want to split this file up
22
import { EventType, record } from '@sentry-internal/rrweb';
3-
import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, getActiveSpan, getClient, getRootSpan, spanToJSON } from '@sentry/core';
3+
import {
4+
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
5+
getActiveSpan,
6+
getClient,
7+
getRootSpan,
8+
setTag,
9+
spanToJSON,
10+
} from '@sentry/core';
411
import type { ReplayRecordingMode, Span } from '@sentry/types';
512
import { logger } from './util/logger';
613

@@ -1226,28 +1233,13 @@ export class ReplayContainer implements ReplayContainerInterface {
12261233
return;
12271234
}
12281235

1229-
const start = this.session.started;
1230-
const now = Date.now();
1231-
const duration = now - start;
1232-
12331236
// A flush is about to happen, cancel any queued flushes
12341237
this._debouncedFlush.cancel();
12351238

1236-
// If session is too short, or too long (allow some wiggle room over maxReplayDuration), do not send it
1237-
// This _should_ not happen, but it may happen if flush is triggered due to a page activity change or similar
1238-
const tooShort = duration < this._options.minReplayDuration;
1239-
const tooLong = duration > this._options.maxReplayDuration + 5_000;
1240-
if (tooShort || tooLong) {
1241-
DEBUG_BUILD &&
1242-
logger.info(
1243-
`Session duration (${Math.floor(duration / 1000)}s) is too ${
1244-
tooShort ? 'short' : 'long'
1245-
}, not sending replay.`,
1246-
);
1239+
const isValidDuration = this._checkReplayDurationDuringFlush();
12471240

1248-
if (tooShort) {
1249-
this._debouncedFlush();
1250-
}
1241+
// XXX: disregard durations for buffer mode for debug purposes
1242+
if (!isValidDuration && this.recordingMode !== 'buffer') {
12511243
return;
12521244
}
12531245

@@ -1283,6 +1275,48 @@ export class ReplayContainer implements ReplayContainerInterface {
12831275
}
12841276
};
12851277

1278+
/**
1279+
* Checks to see if replay duration is within bounds during a flush. If it is
1280+
* too short, will queue up a new flush to prevent short replays.
1281+
*
1282+
* Returns true if duration is ok, false otherwise
1283+
*/
1284+
private _checkReplayDurationDuringFlush(): boolean {
1285+
if (!this.session) {
1286+
return false;
1287+
}
1288+
1289+
const start = this.session.started;
1290+
const now = Date.now();
1291+
const duration = now - start;
1292+
1293+
// If session is too short, or too long (allow some wiggle room over maxReplayDuration), do not send it
1294+
// This _should_ not happen, but it may happen if flush is triggered due to a page activity change or similar
1295+
const tooShort = duration < this._options.minReplayDuration;
1296+
const tooLong = duration > this._options.maxReplayDuration + 5_000;
1297+
if (tooShort || tooLong) {
1298+
DEBUG_BUILD &&
1299+
logger.info(
1300+
`Session duration (${Math.floor(duration / 1000)}s) is too ${
1301+
tooShort ? 'short' : 'long'
1302+
}, not sending replay.`,
1303+
);
1304+
1305+
if (tooShort) {
1306+
this._debouncedFlush();
1307+
}
1308+
1309+
// XXX: disregard durations for buffer mode for debug purposes
1310+
if (this.recordingMode === 'buffer') {
1311+
setTag(`replay.${tooShort ? 'tooShort' : 'tooLong'}`, true);
1312+
}
1313+
1314+
return false;
1315+
}
1316+
1317+
return true;
1318+
}
1319+
12861320
/** Save the session, if it is sticky */
12871321
private _maybeSaveSession(): void {
12881322
if (this.session && this._options.stickySession) {

packages/replay-internal/src/util/handleRecordingEmit.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export function getHandleRecordingEmit(replay: ReplayContainer): RecordingEmitCa
4343
// dependent on this reset.
4444
if (replay.recordingMode === 'buffer' && isCheckout) {
4545
replay.setInitialState();
46+
DEBUG_BUILD && logger.info(`Adding checkout event while in buffer mode (${JSON.stringify(event).length})`);
4647
}
4748

4849
// If the event is not added (e.g. due to being paused, disabled, or out of the max replay duration),
@@ -73,8 +74,13 @@ export function getHandleRecordingEmit(replay: ReplayContainer): RecordingEmitCa
7374

7475
// When in buffer mode, make sure we adjust the session started date to the current earliest event of the buffer
7576
// this should usually be the timestamp of the checkout event, but to be safe...
76-
if (replay.recordingMode === 'buffer' && session && replay.eventBuffer) {
77-
const earliestEvent = replay.eventBuffer.getEarliestTimestamp();
77+
if (replay.recordingMode === 'buffer' && session) {
78+
DEBUG_BUILD &&
79+
replay.eventBuffer &&
80+
logger.info('Attempting to fetch earliest event from event buffer type: ', replay.eventBuffer.type);
81+
82+
const earliestEvent = replay.eventBuffer && replay.eventBuffer.getEarliestTimestamp();
83+
7884
if (earliestEvent) {
7985
DEBUG_BUILD &&
8086
logger.info(`Updating session start time to earliest event in buffer to ${new Date(earliestEvent)}`);
@@ -84,6 +90,9 @@ export function getHandleRecordingEmit(replay: ReplayContainer): RecordingEmitCa
8490
if (replay.getOptions().stickySession) {
8591
saveSession(session);
8692
}
93+
} else {
94+
// XXX(billy): debugging
95+
DEBUG_BUILD && logger.warn('Unable to find earliest event in event buffer');
8796
}
8897
}
8998

0 commit comments

Comments
 (0)