-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat(replay): Save replay in sessionStorage and attempt to re-request if necessary #6698
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
Conversation
size-limit report 📦
|
d95e731
to
aeb40b9
Compare
// Replays have separate set of breadcrumbs, do not include breadcrumbs | ||
// from core SDK | ||
delete event.breadcrumbs; |
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 this to the SDK where we send the replay request (right after we prepare event), otherwise the restoration request happens before this handler is attached.
packages/replay/src/replay.ts
Outdated
const pendingEvent = await getPendingReplay({ useCompression }); | ||
if (pendingEvent) { | ||
await this.sendReplayRequest(pendingEvent); |
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.
This happens before we attach any listeners at all and will block until we make this request. Note this calls the function that does not retry, but maybe it should?
} else if (state === FlushState.COMPLETE) { | ||
WINDOW.sessionStorage.removeItem(PENDING_REPLAY_STATUS_KEY); | ||
} else { | ||
WINDOW.sessionStorage.setItem(PENDING_REPLAY_STATUS_KEY, state); |
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.
After reading this... this probably isn't necessary. It would let us know at which stage the user dropped off.
a0059a4
to
9624e4e
Compare
c58b7c0
to
efa539c
Compare
be829e1
to
2756060
Compare
9624e4e
to
d6a5f05
Compare
d6a5f05
to
7f3fa0b
Compare
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.
Just talked with @mydea about this. I'm a little bit concerned that we might not always clear the pending replays from session storage. Which is proabaly generally a problem with everything we try to keep in session storage. We talked a little bit about using a SharedWorker
instead which would live across tabs but stay alive on unloads. So we'd probably need to have a map of session ids and eventbuffers but it might be worth a try. WDYT?
Regardless, I went over the PR and code-wise I think it looks good. I'd just want to avoid the enum if possible (see comment).
@@ -18,6 +18,17 @@ export interface SendReplayData { | |||
options: ReplayPluginOptions; | |||
} | |||
|
|||
export enum FlushState { |
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.
We should generally avoid enums as they have a pretty hefty bundle size impact (more details)
We could use string constants instead, something along these lines:
export type FlushState = 'start' | 'sent-request' | 'complete' | 'error'
WDYT?
After thinking about this a bit, and trying a few things, I actually think a nicer solution for this problem would be to avoid compression when we need to send events ASAP (e.g. on unload). I'll try to write up a proof of concept to illustrate what I mean. |
@mydea yeah I had that idea in mind as well - we would avoid keeping state in session storage and segments should be small enough that it's fine to have them uncompressed. |
This PR illustrates how that could work - actually it's quite straightforward, "only" thing missing is to make sure to use the |
I'm going to close this out for now as we have improved segment quality a significant amount already. This PR will add a bit of complexity and overhead, so let's hold off unless we really deem it necessary. |
Save replay request parameters to sessionStorage before we
await
any async functions when flushing. This is cleared after each successful request. If integration is reloaded and there is data in sessionStorage, we will attempt a new request to send saved data.This happens because when a user refreshes, we have some async tasks (e.g. compression worker) which will not resolve before the page is unloaded. However, we can retrieve the raw events from the worker and save to sessionStorage as those are all synchronous tasks.
Should help address #6533
Requires #6696, #6695, #6699