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

Architecture discussion: Deferred events #860

Open
Juice10 opened this issue Mar 11, 2022 · 1 comment
Open

Architecture discussion: Deferred events #860

Juice10 opened this issue Mar 11, 2022 · 1 comment

Comments

@Juice10
Copy link
Contributor

Juice10 commented Mar 11, 2022

Summary

rrweb currently doesn't have a preferred way of dealing with events that take a while to process before they are fully captured. Let's discuss strategies to deal with these types of events.

Reasons

Handling async commands

Some commands are only available async so we have to wait till they are complete before we're able to save the event.

Keeping event order

In some cases event order is important.
Example of two events happening within the timespan of one second:

  1. SLOW EVENT: adds element A (capture takes 10 seconds to complete)
  2. FAST EVENT: does something with element A (takes 1ms to complete)
    If we were to emit these events when they are completed processing they'll be saved out of order, breaking the replay.

Real world use cases

Performance / web workers

Some heavy synchronous tasks we do (e.g. canvas.toDataURL, JSON.stringify base64 encoding) cause jank. Moving this to a web worker would mean having a wait longer for the results but a much smoother user experience during recording.

Image inlining

When using fetch to request images to inline we have to wait for the request to complete. Most of the time this will be pretty fast as the browser probably still has the image in cache, but this is an async operation.
More info: #836 (comment)

Stylesheet inlining

Some stylesheets take a while to load and currently their contents get missed from the inline script: #764

Canvas mutation serialization

Serializing some arguments of canvas calls is only available async. Examples: Blob#arrayBuffer() Canvas#toBlob()
With canvas recording order is incredibly important, if two events get recorded out of order the whole recording would (silently) break yielding an empty canvas (which is a PITA to debug).

@Juice10
Copy link
Contributor Author

Juice10 commented Mar 11, 2022

Potential solutions

Event

Events wrapped in a promise

When events are emitted they are wrapped in a promise and its up to the end user to await for them to finish.

Attributes wrapped in a promise

For example, attribute pending:

{
  timestamp: 1234,
  data: {
    content: [Promise]
  }
}

When finished it could overwrite the attribute:

{
  timestamp: 1234,
  data: {
    content: 'base64encodedstring'
  }
}

Recording API

Two emit methods on record

One for the events at the moment they occur and one for the events when they are completed.

One emit method: emit in order of occurrence

Keeps all events in order, emits them one at a time (oldest first), but only emits the event once it's fully processed. Keeps a buffer of events that might be fully processed but occurred at a later time.

One emit method: emits unordered when processed but includes original timestamp (or order id)

In this case it's up to the (live) replay to order everything correctly.

Two part events

Suggested by @Yuyz0112: split an event into two events. One event at time of occurrence containing as much data as possible. A second one later, containing the contents that where still being processed.
I think this is a great match for inlining images, but not such a good match for serializing canvas calls.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant