diff --git a/frontend/__snapshots__/scenes-app-data-management--ingestion-warnings--dark.png b/frontend/__snapshots__/scenes-app-data-management--ingestion-warnings--dark.png
index 915d1d37460d0..f1e9cc5f96fea 100644
Binary files a/frontend/__snapshots__/scenes-app-data-management--ingestion-warnings--dark.png and b/frontend/__snapshots__/scenes-app-data-management--ingestion-warnings--dark.png differ
diff --git a/frontend/__snapshots__/scenes-app-data-management--ingestion-warnings--light.png b/frontend/__snapshots__/scenes-app-data-management--ingestion-warnings--light.png
index 33b26bef7b64c..0370f8bb53c4f 100644
Binary files a/frontend/__snapshots__/scenes-app-data-management--ingestion-warnings--light.png and b/frontend/__snapshots__/scenes-app-data-management--ingestion-warnings--light.png differ
diff --git a/frontend/src/scenes/data-management/ingestion-warnings/IngestionWarningsView.tsx b/frontend/src/scenes/data-management/ingestion-warnings/IngestionWarningsView.tsx
index 7c2e1c57cfdad..acfbda51301c8 100644
--- a/frontend/src/scenes/data-management/ingestion-warnings/IngestionWarningsView.tsx
+++ b/frontend/src/scenes/data-management/ingestion-warnings/IngestionWarningsView.tsx
@@ -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 = {
@@ -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:
+
+ - session_id: {details.session_id}
+
+
+ }
+ data-attr="message-too-large-view-recording"
+ >
+ View recording
+
+
+ >
+ )
+ },
}
export function IngestionWarningsView(): JSX.Element {
diff --git a/frontend/src/scenes/data-management/ingestion-warnings/__mocks__/ingestion-warnings-response.ts b/frontend/src/scenes/data-management/ingestion-warnings/__mocks__/ingestion-warnings-response.ts
index 473f001793616..4d37b19e3284b 100644
--- a/frontend/src/scenes/data-management/ingestion-warnings/__mocks__/ingestion-warnings-response.ts
+++ b/frontend/src/scenes/data-management/ingestion-warnings/__mocks__/ingestion-warnings-response.ts
@@ -3,6 +3,22 @@ import { dayjs } from 'lib/dayjs'
export const ingestionWarningsResponse = (baseTime: dayjs.Dayjs): { results: Record } => {
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'),
diff --git a/plugin-server/src/main/ingestion-queues/session-recording/process-event.ts b/plugin-server/src/main/ingestion-queues/session-recording/process-event.ts
index a729fb23fcff6..81cc72f3c3eb9 100644
--- a/plugin-server/src/main/ingestion-queues/session-recording/process-event.ts
+++ b/plugin-server/src/main/ingestion-queues/session-recording/process-event.ts
@@ -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
@@ -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
@@ -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)
@@ -330,5 +336,5 @@ export const createSessionReplayEvent = (
snapshot_source: snapshot_source || 'web',
}
- return data
+ return { event: data, warnings }
}
diff --git a/plugin-server/src/main/ingestion-queues/session-recording/services/replay-events-ingester.ts b/plugin-server/src/main/ingestion-queues/session-recording/services/replay-events-ingester.ts
index 669a8edc72a90..c97539b796c14 100644
--- a/plugin-server/src/main/ingestion-queues/session-recording/services/replay-events-ingester.ts
+++ b/plugin-server/src/main/ingestion-queues/session-recording/services/replay-events-ingester.ts
@@ -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,
@@ -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: {
diff --git a/plugin-server/tests/main/ingestion-queues/session-recording/process-event.test.ts b/plugin-server/tests/main/ingestion-queues/session-recording/process-event.test.ts
index d74d3a2de9e23..23c89d7c64555 100644
--- a/plugin-server/tests/main/ingestion-queues/session-recording/process-event.test.ts
+++ b/plugin-server/tests/main/ingestion-queues/session-recording/process-event.test.ts
@@ -33,6 +33,7 @@ describe('session recording process event', () => {
| 'message_count'
| 'snapshot_source'
>
+ expectedWarnings: string[]
}[] = [
{
testDescription: 'click and mouse counts are detected',
@@ -68,6 +69,7 @@ describe('session recording process event', () => {
message_count: 1,
snapshot_source: 'web',
},
+ expectedWarnings: [],
},
{
testDescription: 'keyboard press is detected',
@@ -91,6 +93,7 @@ describe('session recording process event', () => {
message_count: 1,
snapshot_source: 'web',
},
+ expectedWarnings: [],
},
{
testDescription: 'console log entries are counted',
@@ -179,6 +182,7 @@ describe('session recording process event', () => {
message_count: 1,
snapshot_source: 'web',
},
+ expectedWarnings: [],
},
{
testDescription: 'url can be detected in meta event',
@@ -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',
@@ -257,6 +262,7 @@ describe('session recording process event', () => {
message_count: 1,
snapshot_source: 'web',
},
+ expectedWarnings: [],
},
{
testDescription: 'first url detection can use payload url',
@@ -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',
@@ -330,6 +337,7 @@ describe('session recording process event', () => {
message_count: 1,
snapshot_source: 'web',
},
+ expectedWarnings: [],
},
{
testDescription: 'overlapping windows are summed separately for activity',
@@ -361,6 +369,7 @@ describe('session recording process event', () => {
message_count: 1,
snapshot_source: 'web',
},
+ expectedWarnings: [],
},
{
testDescription: 'mobile snapshot source is stored',
@@ -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',
@@ -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',