move stitching calculation to server side

This commit is contained in:
Francis Cao
2026-03-03 16:28:09 -08:00
parent 4ecef2e290
commit b2d45613b5
4 changed files with 22 additions and 22 deletions
@@ -4,24 +4,6 @@ import { useEffect, useRef, useState } from 'react';
import { useMobile } from '@/components/hooks';
import 'rrweb-player/dist/style.css';
function normalizeEvents(events: any[], maxGapMs = 60_000): any[] {
if (events.length < 2) return events;
const sorted = [...events].sort((a, b) => a.timestamp - b.timestamp);
let shift = 0;
const result: any[] = [sorted[0]];
for (let i = 1; i < sorted.length; i++) {
const gap = sorted[i].timestamp - sorted[i - 1].timestamp;
if (gap > maxGapMs) {
shift += gap - 1000;
}
result.push(shift > 0 ? { ...sorted[i], timestamp: sorted[i].timestamp - shift } : sorted[i]);
}
return result;
}
export function ReplayPlayer({ events }: { events: any[] }) {
const containerRef = useRef<HTMLDivElement>(null);
const playerRef = useRef<any>(null);
@@ -44,7 +26,7 @@ export function ReplayPlayer({ events }: { events: any[] }) {
playerRef.current = new RRWebPlayer({
target: containerRef.current,
props: {
events: normalizeEvents(events),
events: events,
width: playerWidth,
height: playerHeight,
autoPlay: false,
@@ -1,3 +1,4 @@
import { stitchChunkEvents } from '@/lib/replay';
import { parseRequest } from '@/lib/request';
import { json, unauthorized } from '@/lib/response';
import { canViewWebsite } from '@/permissions';
@@ -21,7 +22,7 @@ export async function GET(
const chunks = await getReplayChunks(websiteId, sessionId);
const allEvents = chunks.flatMap(chunk => chunk.events);
const allEvents = stitchChunkEvents(chunks);
const startedAt = chunks.length > 0 ? chunks[0].startedAt : null;
const endedAt = chunks.length > 0 ? chunks[chunks.length - 1].endedAt : null;
+17
View File
@@ -0,0 +1,17 @@
import type { ReplayChunk } from '@/queries/sql';
export function stitchChunkEvents(chunks: ReplayChunk[]): any[] {
if (!chunks.length) return [];
const sorted = [...chunks].sort((a, b) => a.startedAt.getTime() - b.startedAt.getTime());
const result: any[] = [];
let timeOffset = 0;
for (const chunk of sorted) {
const chunkStartMs = chunk.startedAt.getTime();
const events = [...chunk.events].sort((a, b) => a.timestamp - b.timestamp);
for (const event of events) {
result.push({ ...event, timestamp: timeOffset + (event.timestamp - chunkStartMs) });
}
timeOffset += chunk.endedAt.getTime() - chunkStartMs + 1000;
}
return result;
}
+2 -2
View File
@@ -53,7 +53,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
count(sr.replay_id) as "chunkCount",
min(sr.started_at) as "startedAt",
max(sr.ended_at) as "endedAt",
(extract(epoch from max(sr.ended_at) - min(sr.started_at)) * 1000)::bigint as "duration",
sum(extract(epoch from sr.ended_at - sr.started_at) * 1000)::bigint as "duration",
max(sr.created_at) as "createdAt"
from session_replay sr
join session on session.session_id = sr.session_id
@@ -109,7 +109,7 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
count(session_replay.replay_id) as chunkCount,
min(session_replay.started_at) as startedAt,
max(session_replay.ended_at) as endedAt,
toInt64(dateDiff('millisecond', min(session_replay.started_at), max(session_replay.ended_at))) as duration,
toInt64(sum(dateDiff('millisecond', session_replay.started_at, session_replay.ended_at))) as duration,
max(session_replay.created_at) as createdAt
from session_replay
join (