Skip to content

ref(replay): Use SESSION_IDLE_DURATION instead of VISIBILITY_CHANGE_TIMEOUT #7297

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

Merged
merged 1 commit into from
Mar 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions packages/replay/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ export const UNABLE_TO_SEND_REPLAY = 'Unable to send Replay';
// The idle limit for a session
export const SESSION_IDLE_DURATION = 300_000; // 5 minutes in ms

// Grace period to keep a session when a user changes tabs or hides window
export const VISIBILITY_CHANGE_TIMEOUT = SESSION_IDLE_DURATION;

// The maximum length of a session
export const MAX_SESSION_LIFE = 3_600_000; // 60 minutes

Expand Down
22 changes: 8 additions & 14 deletions packages/replay/src/replay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,7 @@ import { captureException, getCurrentHub } from '@sentry/core';
import type { Breadcrumb, ReplayRecordingMode } from '@sentry/types';
import { logger } from '@sentry/utils';

import {
ERROR_CHECKOUT_TIME,
MAX_SESSION_LIFE,
SESSION_IDLE_DURATION,
VISIBILITY_CHANGE_TIMEOUT,
WINDOW,
} from './constants';
import { ERROR_CHECKOUT_TIME, MAX_SESSION_LIFE, SESSION_IDLE_DURATION, WINDOW } from './constants';
import { setupPerformanceObserver } from './coreHandlers/performanceObserver';
import { createEventBuffer } from './eventBuffer';
import { getSession } from './session/getSession';
Expand Down Expand Up @@ -367,7 +361,7 @@ export class ReplayContainer implements ReplayContainerInterface {
* Returns true if session is not expired, false otherwise.
* @hidden
*/
public checkAndHandleExpiredSession(expiry?: number): boolean | void {
public checkAndHandleExpiredSession(): boolean | void {
const oldSessionId = this.getSessionId();

// Prevent starting a new session if the last user activity is older than
Expand All @@ -382,7 +376,7 @@ export class ReplayContainer implements ReplayContainerInterface {

// --- There is recent user activity --- //
// This will create a new session if expired, based on expiry length
if (!this._loadAndCheckSession(expiry)) {
if (!this._loadAndCheckSession()) {
return;
}

Expand Down Expand Up @@ -412,9 +406,9 @@ export class ReplayContainer implements ReplayContainerInterface {
* Loads (or refreshes) the current session.
* Returns false if session is not recorded.
*/
private _loadAndCheckSession(expiry = SESSION_IDLE_DURATION): boolean {
private _loadAndCheckSession(): boolean {
const { type, session } = getSession({
expiry,
expiry: SESSION_IDLE_DURATION,
stickySession: Boolean(this._options.stickySession),
currentSession: this.session,
sessionSampleRate: this._options.sessionSampleRate,
Expand Down Expand Up @@ -626,7 +620,7 @@ export class ReplayContainer implements ReplayContainerInterface {
return;
}

const expired = isSessionExpired(this.session, VISIBILITY_CHANGE_TIMEOUT);
const expired = isSessionExpired(this.session, SESSION_IDLE_DURATION);

if (breadcrumb && !expired) {
this._createCustomBreadcrumb(breadcrumb);
Expand All @@ -646,10 +640,10 @@ export class ReplayContainer implements ReplayContainerInterface {
return;
}

const isSessionActive = this.checkAndHandleExpiredSession(VISIBILITY_CHANGE_TIMEOUT);
const isSessionActive = this.checkAndHandleExpiredSession();

if (!isSessionActive) {
// If the user has come back to the page within VISIBILITY_CHANGE_TIMEOUT
// If the user has come back to the page within SESSION_IDLE_DURATION
// ms, we will re-use the existing session, otherwise create a new
// session
__DEBUG_BUILD__ && logger.log('[Replay] Document has become active, but session has expired');
Expand Down
10 changes: 5 additions & 5 deletions packages/replay/test/integration/errorSampleRate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
ERROR_CHECKOUT_TIME,
MAX_SESSION_LIFE,
REPLAY_SESSION_KEY,
VISIBILITY_CHANGE_TIMEOUT,
SESSION_IDLE_DURATION,
WINDOW,
} from '../../src/constants';
import type { ReplayContainer } from '../../src/replay';
Expand Down Expand Up @@ -154,15 +154,15 @@ describe('Integration | errorSampleRate', () => {
});
});

it('does not send a replay when triggering a full dom snapshot when document becomes visible after [VISIBILITY_CHANGE_TIMEOUT]ms', async () => {
it('does not send a replay when triggering a full dom snapshot when document becomes visible after [SESSION_IDLE_DURATION]ms', async () => {
Object.defineProperty(document, 'visibilityState', {
configurable: true,
get: function () {
return 'visible';
},
});

jest.advanceTimersByTime(VISIBILITY_CHANGE_TIMEOUT + 1);
jest.advanceTimersByTime(SESSION_IDLE_DURATION + 1);

document.dispatchEvent(new Event('visibilitychange'));

Expand All @@ -186,8 +186,8 @@ describe('Integration | errorSampleRate', () => {

expect(replay).not.toHaveLastSentReplay();

// User comes back before `VISIBILITY_CHANGE_TIMEOUT` elapses
jest.advanceTimersByTime(VISIBILITY_CHANGE_TIMEOUT - 100);
// User comes back before `SESSION_IDLE_DURATION` elapses
jest.advanceTimersByTime(SESSION_IDLE_DURATION - 100);
Object.defineProperty(document, 'visibilityState', {
configurable: true,
get: function () {
Expand Down
4 changes: 2 additions & 2 deletions packages/replay/test/integration/events.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ describe('Integration | events', () => {
// Create a new session and clear mocks because a segment (from initial
// checkout) will have already been uploaded by the time the tests run
clearSession(replay);
replay['_loadAndCheckSession'](0);
replay['_loadAndCheckSession']();
mockTransportSend.mockClear();
});

Expand Down Expand Up @@ -93,7 +93,7 @@ describe('Integration | events', () => {

it('has correct timestamps when there are events earlier than initial timestamp', async function () {
clearSession(replay);
replay['_loadAndCheckSession'](0);
replay['_loadAndCheckSession']();
mockTransportSend.mockClear();
Object.defineProperty(document, 'visibilityState', {
configurable: true,
Expand Down
4 changes: 2 additions & 2 deletions packages/replay/test/integration/flush.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as SentryUtils from '@sentry/utils';

import { DEFAULT_FLUSH_MIN_DELAY, SESSION_IDLE_DURATION, WINDOW } from '../../src/constants';
import { DEFAULT_FLUSH_MIN_DELAY, WINDOW } from '../../src/constants';
import type { ReplayContainer } from '../../src/replay';
import type { EventBuffer } from '../../src/types';
import * as AddMemoryEntry from '../../src/util/addMemoryEntry';
Expand Down Expand Up @@ -95,7 +95,7 @@ describe('Integration | flush', () => {
jest.setSystemTime(new Date(BASE_TIMESTAMP));
sessionStorage.clear();
clearSession(replay);
replay['_loadAndCheckSession'](SESSION_IDLE_DURATION);
replay['_loadAndCheckSession']();
mockRecord.takeFullSnapshot.mockClear();
Object.defineProperty(WINDOW, 'location', {
value: prevLocation,
Expand Down
6 changes: 3 additions & 3 deletions packages/replay/test/integration/rateLimiting.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getCurrentHub } from '@sentry/core';
import type { Transport } from '@sentry/types';

import { DEFAULT_FLUSH_MIN_DELAY, SESSION_IDLE_DURATION } from '../../src/constants';
import { DEFAULT_FLUSH_MIN_DELAY } from '../../src/constants';
import type { ReplayContainer } from '../../src/replay';
import * as SendReplayRequest from '../../src/util/sendReplayRequest';
import { BASE_TIMESTAMP, mockSdk } from '../index';
Expand Down Expand Up @@ -46,7 +46,7 @@ describe('Integration | rate-limiting behaviour', () => {
// Create a new session and clear mocks because a segment (from initial
// checkout) will have already been uploaded by the time the tests run
clearSession(replay);
replay['_loadAndCheckSession'](0);
replay['_loadAndCheckSession']();

mockSendReplayRequest.mockClear();
});
Expand All @@ -57,7 +57,7 @@ describe('Integration | rate-limiting behaviour', () => {
jest.setSystemTime(new Date(BASE_TIMESTAMP));
clearSession(replay);
jest.clearAllMocks();
replay['_loadAndCheckSession'](SESSION_IDLE_DURATION);
replay['_loadAndCheckSession']();
});

afterAll(() => {
Expand Down
6 changes: 3 additions & 3 deletions packages/replay/test/integration/sendReplayEvent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as SentryCore from '@sentry/core';
import type { Transport } from '@sentry/types';
import * as SentryUtils from '@sentry/utils';

import { DEFAULT_FLUSH_MIN_DELAY, SESSION_IDLE_DURATION, WINDOW } from '../../src/constants';
import { DEFAULT_FLUSH_MIN_DELAY, WINDOW } from '../../src/constants';
import type { ReplayContainer } from '../../src/replay';
import { addEvent } from '../../src/util/addEvent';
import * as SendReplayRequest from '../../src/util/sendReplayRequest';
Expand Down Expand Up @@ -59,7 +59,7 @@ describe('Integration | sendReplayEvent', () => {
// Create a new session and clear mocks because a segment (from initial
// checkout) will have already been uploaded by the time the tests run
clearSession(replay);
replay['_loadAndCheckSession'](0);
replay['_loadAndCheckSession']();

mockSendReplayRequest.mockClear();
});
Expand All @@ -69,7 +69,7 @@ describe('Integration | sendReplayEvent', () => {
await new Promise(process.nextTick);
jest.setSystemTime(new Date(BASE_TIMESTAMP));
clearSession(replay);
replay['_loadAndCheckSession'](SESSION_IDLE_DURATION);
replay['_loadAndCheckSession']();
});

afterAll(() => {
Expand Down
28 changes: 14 additions & 14 deletions packages/replay/test/integration/session.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
DEFAULT_FLUSH_MIN_DELAY,
MAX_SESSION_LIFE,
REPLAY_SESSION_KEY,
VISIBILITY_CHANGE_TIMEOUT,
SESSION_IDLE_DURATION,
WINDOW,
} from '../../src/constants';
import type { ReplayContainer } from '../../src/replay';
Expand Down Expand Up @@ -55,7 +55,7 @@ describe('Integration | session', () => {
});
});

it('creates a new session and triggers a full dom snapshot when document becomes visible after [VISIBILITY_CHANGE_TIMEOUT]ms', () => {
it('creates a new session and triggers a full dom snapshot when document becomes visible after [SESSION_IDLE_DURATION]ms', () => {
Object.defineProperty(document, 'visibilityState', {
configurable: true,
get: function () {
Expand All @@ -65,7 +65,7 @@ describe('Integration | session', () => {

const initialSession = replay.session;

jest.advanceTimersByTime(VISIBILITY_CHANGE_TIMEOUT + 1);
jest.advanceTimersByTime(SESSION_IDLE_DURATION + 1);

document.dispatchEvent(new Event('visibilitychange'));

Expand All @@ -75,7 +75,7 @@ describe('Integration | session', () => {
expect(replay).not.toHaveSameSession(initialSession);
});

it('does not create a new session if user hides the tab and comes back within [VISIBILITY_CHANGE_TIMEOUT] seconds', () => {
it('does not create a new session if user hides the tab and comes back within [SESSION_IDLE_DURATION] seconds', () => {
const initialSession = replay.session;

Object.defineProperty(document, 'visibilityState', {
Expand All @@ -88,8 +88,8 @@ describe('Integration | session', () => {
expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled();
expect(replay).toHaveSameSession(initialSession);

// User comes back before `VISIBILITY_CHANGE_TIMEOUT` elapses
jest.advanceTimersByTime(VISIBILITY_CHANGE_TIMEOUT - 1);
// User comes back before `SESSION_IDLE_DURATION` elapses
jest.advanceTimersByTime(SESSION_IDLE_DURATION - 1);
Object.defineProperty(document, 'visibilityState', {
configurable: true,
get: function () {
Expand Down Expand Up @@ -184,7 +184,7 @@ describe('Integration | session', () => {
expect(replay.session).toBe(undefined);
});

it('creates a new session and triggers a full dom snapshot when document becomes visible after [VISIBILITY_CHANGE_TIMEOUT]ms', () => {
it('creates a new session and triggers a full dom snapshot when document becomes visible after [SESSION_IDLE_DURATION]ms', () => {
Object.defineProperty(document, 'visibilityState', {
configurable: true,
get: function () {
Expand All @@ -194,7 +194,7 @@ describe('Integration | session', () => {

const initialSession = replay.session;

jest.advanceTimersByTime(VISIBILITY_CHANGE_TIMEOUT + 1);
jest.advanceTimersByTime(SESSION_IDLE_DURATION + 1);

document.dispatchEvent(new Event('visibilitychange'));

Expand All @@ -204,7 +204,7 @@ describe('Integration | session', () => {
expect(replay).not.toHaveSameSession(initialSession);
});

it('creates a new session and triggers a full dom snapshot when document becomes focused after [VISIBILITY_CHANGE_TIMEOUT]ms', () => {
it('creates a new session and triggers a full dom snapshot when document becomes focused after [SESSION_IDLE_DURATION]ms', () => {
Object.defineProperty(document, 'visibilityState', {
configurable: true,
get: function () {
Expand All @@ -214,7 +214,7 @@ describe('Integration | session', () => {

const initialSession = replay.session;

jest.advanceTimersByTime(VISIBILITY_CHANGE_TIMEOUT + 1);
jest.advanceTimersByTime(SESSION_IDLE_DURATION + 1);

WINDOW.dispatchEvent(new Event('focus'));

Expand All @@ -224,7 +224,7 @@ describe('Integration | session', () => {
expect(replay).not.toHaveSameSession(initialSession);
});

it('does not create a new session if user hides the tab and comes back within [VISIBILITY_CHANGE_TIMEOUT] seconds', () => {
it('does not create a new session if user hides the tab and comes back within [SESSION_IDLE_DURATION] seconds', () => {
const initialSession = replay.session;

Object.defineProperty(document, 'visibilityState', {
Expand All @@ -237,8 +237,8 @@ describe('Integration | session', () => {
expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled();
expect(replay).toHaveSameSession(initialSession);

// User comes back before `VISIBILITY_CHANGE_TIMEOUT` elapses
jest.advanceTimersByTime(VISIBILITY_CHANGE_TIMEOUT - 1);
// User comes back before `SESSION_IDLE_DURATION` elapses
jest.advanceTimersByTime(SESSION_IDLE_DURATION - 1);
Object.defineProperty(document, 'visibilityState', {
configurable: true,
get: function () {
Expand Down Expand Up @@ -451,7 +451,7 @@ describe('Integration | session', () => {

it('increases segment id after each event', async () => {
clearSession(replay);
replay['_loadAndCheckSession'](0);
replay['_loadAndCheckSession']();

Object.defineProperty(document, 'visibilityState', {
configurable: true,
Expand Down
4 changes: 2 additions & 2 deletions packages/replay/test/integration/stop.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as SentryUtils from '@sentry/utils';

import type { Replay } from '../../src';
import { SESSION_IDLE_DURATION, WINDOW } from '../../src/constants';
import { WINDOW } from '../../src/constants';
import type { ReplayContainer } from '../../src/replay';
import { addEvent } from '../../src/util/addEvent';
// mock functions need to be imported first
Expand Down Expand Up @@ -44,7 +44,7 @@ describe('Integration | stop', () => {
jest.setSystemTime(new Date(BASE_TIMESTAMP));
sessionStorage.clear();
clearSession(replay);
replay['_loadAndCheckSession'](SESSION_IDLE_DURATION);
replay['_loadAndCheckSession']();
mockRecord.takeFullSnapshot.mockClear();
mockAddInstrumentationHandler.mockClear();
Object.defineProperty(WINDOW, 'location', {
Expand Down
3 changes: 1 addition & 2 deletions packages/replay/test/utils/setupReplayContainer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { SESSION_IDLE_DURATION } from '../../src/constants';
import { createEventBuffer } from '../../src/eventBuffer';
import { ReplayContainer } from '../../src/replay';
import type { RecordingOptions, ReplayPluginOptions } from '../../src/types';
Expand Down Expand Up @@ -28,7 +27,7 @@ export function setupReplayContainer({

clearSession(replay);
replay['_setInitialState']();
replay['_loadAndCheckSession'](SESSION_IDLE_DURATION);
replay['_loadAndCheckSession']();
replay['_isEnabled'] = true;
replay.eventBuffer = createEventBuffer({
useCompression: options?.useCompression || false,
Expand Down