-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat(replay): Change stop()
to flush and remove current session
#7741
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
Changes from 6 commits
5641265
14b4b5d
cd3c1d6
ac8a4b6
3fab0b2
45996ed
756f716
d1990e1
aa34bc5
b43de34
b2c8ffc
d04a90d
061ead9
fd448a6
dc8cd6c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ import { logger } from '@sentry/utils'; | |
import { ERROR_CHECKOUT_TIME, MAX_SESSION_LIFE, SESSION_IDLE_DURATION, WINDOW } from './constants'; | ||
import { setupPerformanceObserver } from './coreHandlers/performanceObserver'; | ||
import { createEventBuffer } from './eventBuffer'; | ||
import { clearSession } from './session/clearSession'; | ||
import { getSession } from './session/getSession'; | ||
import { saveSession } from './session/saveSession'; | ||
import type { | ||
|
@@ -85,6 +86,11 @@ export class ReplayContainer implements ReplayContainerInterface { | |
*/ | ||
private _isEnabled: boolean = false; | ||
|
||
/** | ||
* If true, will flush regardless of `_isEnabled` property | ||
*/ | ||
private _shouldFinalFlush: boolean = false; | ||
|
||
/** | ||
* Paused is a state where: | ||
* - DOM Recording is not listening at all | ||
|
@@ -237,7 +243,7 @@ export class ReplayContainer implements ReplayContainerInterface { | |
* Currently, this needs to be manually called (e.g. for tests). Sentry SDK | ||
* does not support a teardown | ||
*/ | ||
public stop(reason?: string): void { | ||
public async stop(reason?: string): Promise<void> { | ||
if (!this._isEnabled) { | ||
return; | ||
} | ||
|
@@ -253,14 +259,28 @@ export class ReplayContainer implements ReplayContainerInterface { | |
log(msg); | ||
} | ||
|
||
// Set this property so that it ignores `_isEnabled` = false | ||
// We can't move `_isEnabled` after awaiting a flush, otherwise we can | ||
// enter into an infinite loop when `stop()` is called while flushing. | ||
this._shouldFinalFlush = true; | ||
this._isEnabled = false; | ||
this._removeListeners(); | ||
this.stopRecording(); | ||
|
||
// Flush event buffer before stopping | ||
await this.flushImmediate(); | ||
|
||
// After flush, destroy event buffer | ||
this.eventBuffer && this.eventBuffer.destroy(); | ||
this.eventBuffer = null; | ||
this._debouncedFlush.cancel(); | ||
|
||
// Clear session from session storage, note this means if a new session | ||
// is started after, it will not have `previousSessionId` | ||
clearSession(this); | ||
} catch (err) { | ||
this._handleException(err); | ||
} finally { | ||
this._shouldFinalFlush = false; | ||
} | ||
} | ||
|
||
|
@@ -466,7 +486,7 @@ export class ReplayContainer implements ReplayContainerInterface { | |
this.session = session; | ||
|
||
if (!this.session.sampled) { | ||
this.stop('session unsampled'); | ||
void this.stop('session unsampled'); | ||
return false; | ||
} | ||
|
||
|
@@ -760,7 +780,7 @@ export class ReplayContainer implements ReplayContainerInterface { | |
// This means we retried 3 times and all of them failed, | ||
// or we ran into a problem we don't want to retry, like rate limiting. | ||
// In this case, we want to completely stop the replay - otherwise, we may get inconsistent segments | ||
this.stop('sendReplay'); | ||
void this.stop('sendReplay'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just noticed this @mydea -- I think this is ok since There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it should be fine since |
||
|
||
const client = getCurrentHub().getClient(); | ||
|
||
|
@@ -775,7 +795,7 @@ export class ReplayContainer implements ReplayContainerInterface { | |
* can be active at a time. Do not call this directly. | ||
*/ | ||
private _flush: () => Promise<void> = async () => { | ||
if (!this._isEnabled) { | ||
if (!this._isEnabled && !this._shouldFinalFlush) { | ||
// This can happen if e.g. the replay was stopped because of exceeding the retry limit | ||
return; | ||
} | ||
|
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.
Hmm, I don't really like this flag - I would prefer we find a way to solve this by e.g. passing an argument to
flushImmediate()
or making a separate function for this type of flushing to ensure this. Seems prone to run out of sync, be accidentally true/false when we don't mean it to, etc.One option I could see is to just do:
Or something along these lines? IMHO as we are stopping here anyhow, we don't necessarily need to run the debounced flush here at all, allowing us to separate this a bit better?
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.
updated, instead of creating a private method, I just kept the flush calls inline as it isn't used elsewhere.