-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(replay): Fix canvas replays when seeking without actively playing #68646
fix(replay): Fix canvas replays when seeking without actively playing #68646
Conversation
This fixes loading a canvas replay when the replay is not actively playing. Previously we had assumed that when seeking, prior events do not matter, but this assumption is wrong when there are more than one canvases. We also need to queue up events to be processed until after `onBuild` is called, as the target event node will not exist in the mirror DOM yet.
Bundle ReportChanges will increase total bundle size by 10.04kB ⬆️
|
const source = replayer.getMirror().getNode(e.data.id); | ||
const target = | ||
canvases.get(e.data.id) || | ||
(source && cloneCanvas(e.data.id, source as HTMLCanvasElement)); | ||
|
||
// No canvas found for id... this isn't reliably reproducible and not | ||
// exactly sure why it flakes. Saving as metric to keep an eye on it. | ||
if (!target) { | ||
Sentry.metrics.increment('replay.canvas_player.no_canvas_id'); | ||
return; | ||
} | ||
|
||
await canvasMutation({ | ||
event: e, | ||
mutation: e.data, | ||
target, | ||
imageMap, | ||
canvasEventMap, | ||
errorHandler: (err: unknown) => { | ||
if (err instanceof Error) { | ||
Sentry.captureException(err); | ||
} else { | ||
Sentry.metrics.increment('replay.canvas_player.error_canvas_mutation'); | ||
} | ||
}, | ||
}); | ||
|
||
const img = containers.get(e.data.id); | ||
if (img) { | ||
img.src = target.toDataURL(); | ||
img.style.maxWidth = '100%'; | ||
img.style.maxHeight = '100%'; | ||
} | ||
|
||
prune(e); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved here from handler
// See comments at definition of `handleQueue` | ||
const queue = handleQueue.get(id); | ||
handleQueue.set(id, []); | ||
while (queue?.length) { | ||
const queueItem = queue.shift(); | ||
if (!queueItem) { | ||
return; | ||
} | ||
const [event, replayer] = queueItem; | ||
processEvent(event, {shouldPreload: false, replayer}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logic is as follows:
- User seeks somewhere in the timeline
- below handler is called with the events before that spot in timeline with
isSync = true
for all the events - create a list of canvas mutation events, indexed by its node id
- wait until
onBuild
is called with an indexed node id (this happens as rrweb is processing events normally) - process events for the node id that was just built
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New Logic:
- User seeks somewhere in the timeline
- below handler is called with the events before that spot in timeline with isSync = true for all the events
- keep latest event, indexed by its node id
- we want to process the indexed events after all of the isSync events are handled. We do this by calling a debounced function that will attempt to process the latest event for each canvas id. we only remove from queue if event was successfully processed, otherwise ...
- wait until onBuild is called with an indexed node id (this happens as rrweb is processing events normally) a nd process events for the node id that was just built
The video seems to play at some spots when seeking even though it's paused. Screen.Recording.2024-04-11.at.10.47.38.AM.mov |
Ah good catch, I suppose we don't want to drain the entire queue, but only the latest one (per id), otherwise it'll look like it's animating. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
everything works well, lgtm!
Suspect IssuesThis pull request was deployed and Sentry observed the following issues:
Did you find this useful? React with a 👍 or 👎 |
This fixes loading a canvas replay when the replay is not actively playing. Previously we had assumed that when seeking, prior events do not matter, but this assumption is wrong when there are more than one canvases. We also need to queue up events to be processed until after
onBuild
is called, as the target event node will not exist in the mirror DOM yet.Closes https://github.com/getsentry/team-replay/issues/415