Skip to content
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

Merged
merged 6 commits into from
Apr 15, 2024

Conversation

billyvg
Copy link
Member

@billyvg billyvg commented Apr 10, 2024

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

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.
@github-actions github-actions bot added the Scope: Frontend Automatically applied to PRs that change frontend components label Apr 10, 2024
Copy link

codecov bot commented Apr 10, 2024

Bundle Report

Changes will increase total bundle size by 10.04kB ⬆️

Bundle name Size Change
sentry-webpack-bundle-array-push 26.3MB 10.04kB ⬆️

Comment on lines 209 to 243
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);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved here from handler

Comment on lines 266 to 276
// 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});
}
Copy link
Member Author

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

Copy link
Member Author

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

@billyvg billyvg marked this pull request as ready for review April 10, 2024 20:36
@billyvg billyvg requested a review from a team as a code owner April 10, 2024 20:36
@c298lee
Copy link
Member

c298lee commented Apr 11, 2024

The video seems to play at some spots when seeking even though it's paused.
Also in some other spots, the buffering doesn't go away until you move your mouse off the timeline, but that seems unrelated to this change

Screen.Recording.2024-04-11.at.10.47.38.AM.mov

@billyvg
Copy link
Member Author

billyvg commented Apr 11, 2024

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.

Copy link
Member

@c298lee c298lee left a 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!

@billyvg billyvg merged commit 254c70e into master Apr 15, 2024
41 of 42 checks passed
@billyvg billyvg deleted the fix-replay-canvas-load-process-previous-canvas-events branch April 15, 2024 20:02
Copy link

sentry-io bot commented Apr 15, 2024

Suspect Issues

This pull request was deployed and Sentry observed the following issues:

  • ‼️ Error: No canvas found for id processEvent(app/components/replays/canvasRepla... View Issue
  • ‼️ Error: No canvas found for id new InvalidCanvasNodeError(app/components/repla... View Issue
  • ‼️ Error: No canvas found for id processEvent(app/components/replays/canvasRepla... View Issue
  • ‼️ TypeError: Ie.toDataURL is not a function processEvent(app/components/replays/canvasRepla... View Issue

Did you find this useful? React with a 👍 or 👎

@github-actions github-actions bot locked and limited conversation to collaborators May 3, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Scope: Frontend Automatically applied to PRs that change frontend components
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants