Skip to content

Commit

Permalink
feat: message too large ingestion warning (#23315)
Browse files Browse the repository at this point in the history
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
pauldambra and github-actions[bot] authored Jun 28, 2024
1 parent cbf3a61 commit 0a52d30
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 5 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const WARNING_TYPE_TO_DESCRIPTION = {
message_size_too_large: 'Discarded event exceeding 1MB limit',
replay_timestamp_invalid: 'Replay event timestamp is invalid',
replay_timestamp_too_far: 'Replay event timestamp was too far in the future',
replay_message_too_large: 'Replay data was dropped because it was too large to ingest',
}

const WARNING_TYPE_RENDERER = {
Expand Down Expand Up @@ -200,6 +201,34 @@ const WARNING_TYPE_RENDERER = {
</>
)
},
replay_message_too_large: function Render(warning: IngestionWarning): JSX.Element {
const details: {
timestamp: string
session_id: string
} = {
timestamp: warning.details.timestamp,
session_id: warning.details.replayRecord.session_id,
}
return (
<>
Session replay data dropped due to its size, this can cause playback problems:
<ul>
<li>session_id: {details.session_id}</li>
</ul>
<div className="max-w-30 mt-2">
<LemonButton
type="primary"
size="xsmall"
to={urls.replaySingle(details.session_id)}
sideIcon={<IconPlayCircle />}
data-attr="message-too-large-view-recording"
>
View recording
</LemonButton>
</div>
</>
)
},
}

export function IngestionWarningsView(): JSX.Element {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,22 @@ import { dayjs } from 'lib/dayjs'
export const ingestionWarningsResponse = (baseTime: dayjs.Dayjs): { results: Record<string, any> } => {
return {
results: [
{
type: 'replay_message_too_large',
lastSeen: baseTime.subtract(1, 'day'),
sparkline: [[1, baseTime.format('YYYY-MM-DD')]],
warnings: [
{
type: 'replay_message_too_large',
timestamp: baseTime.subtract(1, 'day'),
details: {
timestamp: 'not a date',
replayRecord: { session_id: 'some uuid' },
},
},
],
count: 1,
},
{
type: 'replay_timestamp_invalid',
lastSeen: baseTime.subtract(1, 'day'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ export const createSessionReplayEvent = (
session_id: string,
events: RRWebEvent[],
snapshot_source: string | null
) => {
): { event: SummarizedSessionRecordingEvent; warnings: string[] } => {
const timestamps = getTimestampsFrom(events)

// but every event where chunk index = 0 must have an eventsSummary
Expand All @@ -268,6 +268,8 @@ export const createSessionReplayEvent = (
throw new Error('ignoring an empty session recording event')
}

const warnings: string[] = []

let clickCount = 0
let keypressCount = 0
let mouseActivity = 0
Expand Down Expand Up @@ -303,6 +305,10 @@ export const createSessionReplayEvent = (
consoleErrorCount += 1
}
}

if (event.type === RRWebEventType.Custom && event.data?.tag === 'Message too large') {
warnings.push('replay_message_too_large')
}
})

const activeTime = activeMilliseconds(events)
Expand Down Expand Up @@ -330,5 +336,5 @@ export const createSessionReplayEvent = (
snapshot_source: snapshot_source || 'web',
}

return data
return { event: data, warnings }
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export class ReplayEventsIngester {
try {
const rrwebEvents = Object.values(event.eventsByWindowId).reduce((acc, val) => acc.concat(val), [])

const replayRecord = createSessionReplayEvent(
const { event: replayRecord, warnings } = createSessionReplayEvent(
randomUUID(),
event.team_id,
event.distinct_id,
Expand Down Expand Up @@ -145,6 +145,22 @@ export class ReplayEventsIngester {
return drop('invalid_timestamp')
}
}

await Promise.allSettled(
warnings.map(async (warning) => {
await captureIngestionWarning(
new KafkaProducerWrapper(this.producer),
event.team_id,
warning,
{
replayRecord,
timestamp: replayRecord.first_timestamp,
processingTimestamp: DateTime.now().toISO(),
},
{ key: event.session_id }
)
})
)
} catch (e) {
captureException(e, {
extra: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ describe('session recording process event', () => {
| 'message_count'
| 'snapshot_source'
>
expectedWarnings: string[]
}[] = [
{
testDescription: 'click and mouse counts are detected',
Expand Down Expand Up @@ -68,6 +69,7 @@ describe('session recording process event', () => {
message_count: 1,
snapshot_source: 'web',
},
expectedWarnings: [],
},
{
testDescription: 'keyboard press is detected',
Expand All @@ -91,6 +93,7 @@ describe('session recording process event', () => {
message_count: 1,
snapshot_source: 'web',
},
expectedWarnings: [],
},
{
testDescription: 'console log entries are counted',
Expand Down Expand Up @@ -179,6 +182,7 @@ describe('session recording process event', () => {
message_count: 1,
snapshot_source: 'web',
},
expectedWarnings: [],
},
{
testDescription: 'url can be detected in meta event',
Expand Down Expand Up @@ -216,6 +220,7 @@ describe('session recording process event', () => {
message_count: 1,
snapshot_source: 'web',
},
expectedWarnings: [],
},
{
testDescription: 'first url detection takes the first url whether meta url or payload url',
Expand Down Expand Up @@ -257,6 +262,7 @@ describe('session recording process event', () => {
message_count: 1,
snapshot_source: 'web',
},
expectedWarnings: [],
},
{
testDescription: 'first url detection can use payload url',
Expand Down Expand Up @@ -302,6 +308,7 @@ describe('session recording process event', () => {
message_count: 1,
snapshot_source: 'web',
},
expectedWarnings: [],
},
{
testDescription: 'negative timestamps are not included when picking timestamps',
Expand Down Expand Up @@ -330,6 +337,7 @@ describe('session recording process event', () => {
message_count: 1,
snapshot_source: 'web',
},
expectedWarnings: [],
},
{
testDescription: 'overlapping windows are summed separately for activity',
Expand Down Expand Up @@ -361,6 +369,7 @@ describe('session recording process event', () => {
message_count: 1,
snapshot_source: 'web',
},
expectedWarnings: [],
},
{
testDescription: 'mobile snapshot source is stored',
Expand All @@ -384,13 +393,41 @@ describe('session recording process event', () => {
size: 82,
snapshot_source: 'mobile',
},
expectedWarnings: [],
},
{
testDescription: 'message too large warning is reported',
snapshotData: {
events_summary: [
{ timestamp: 1682449093000, type: 3, data: { source: 2, type: 2 }, windowId: '1' },
{ timestamp: 1682449093000, type: 5, data: { tag: 'Message too large' }, windowId: '1' },
],
},
snapshotSource: 'web',
expected: {
active_milliseconds: 1,
click_count: 1,
console_error_count: 0,
console_log_count: 0,
console_warn_count: 0,
event_count: 2,
first_timestamp: '2023-04-25 18:58:13.000',
first_url: null,
keypress_count: 0,
last_timestamp: '2023-04-25 18:58:13.000',
message_count: 1,
mouse_activity_count: 1,
size: 169,
snapshot_source: 'web',
},
expectedWarnings: ['replay_message_too_large'],
},
]

it.each(sessionReplayEventTestCases)(
'session replay event generation - $testDescription',
({ snapshotData, snapshotSource, expected }) => {
const data = createSessionReplayEvent(
({ snapshotData, snapshotSource, expected, expectedWarnings }) => {
const { event: data, warnings } = createSessionReplayEvent(
'some-id',
12345,
'5AzhubH8uMghFHxXq0phfs14JOjH6SA2Ftr1dzXj7U4',
Expand All @@ -399,6 +436,8 @@ describe('session recording process event', () => {
snapshotSource || null
)

expect(warnings).toStrictEqual(expectedWarnings)

const expectedEvent: SummarizedSessionRecordingEvent = {
distinct_id: '5AzhubH8uMghFHxXq0phfs14JOjH6SA2Ftr1dzXj7U4',
session_id: 'abcf-efg',
Expand Down

0 comments on commit 0a52d30

Please sign in to comment.