Skip to content

Commit

Permalink
feat: Use new shared session for Session Trace feature - NEWRELIC-8662 (
Browse files Browse the repository at this point in the history
#545)

Co-authored-by: Patrick Housley <[email protected]>
Co-authored-by: Jordan Porter <[email protected]>
  • Loading branch information
3 people authored Jun 9, 2023
1 parent 9c2f93d commit dbd995a
Show file tree
Hide file tree
Showing 32 changed files with 479 additions and 454 deletions.
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions src/common/constants/shared-channel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* @file Keeps an object alive that is passed to all feature aggregate modules.
* The purpose is to have a way for communication and signals to relay across features at runtime.
* This object can hold any arbitrary values and should be treated as on-the-fly dynamic.
*/

let onReplayReady
const sessionReplayInitialized = new Promise(resolve => onReplayReady = resolve)

export const sharedChannel = Object.freeze({
onReplayReady,
sessionReplayInitialized
})
23 changes: 17 additions & 6 deletions src/common/harvest/harvest-scheduler.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { SharedContext } from '../context/shared-context'
import { Harvest, getSubmitMethod } from './harvest'
import { subscribeToEOL } from '../unload/eol'
import { getConfigurationValue } from '../config/config'
import { SESSION_EVENTS } from '../session/session-entity'

/**
* Periodically invokes harvest calls and handles retries
Expand Down Expand Up @@ -37,10 +38,16 @@ export class HarvestScheduler extends SharedContext {
// unload if EOL mechanism fires
subscribeToEOL(this.unload.bind(this), getConfigurationValue(this.sharedContext.agentIdentifier, 'allow_bfcache')) // TO DO: remove feature flag after rls stable

// unload if session resets
this.sharedContext?.ee.on('session-reset', this.unload.bind(this))
/* Flush all buffered data if session resets and give up retries. This should be synchronous to ensure that the correct `session` value is sent.
Since session-reset generates a new session ID and the ID is grabbed at send-time, any delays or retries would cause the payload to be sent under
the wrong session ID. */
this.sharedContext?.ee.on(SESSION_EVENTS.RESET, () => this.runHarvest({ forceNoRetry: true }))
}

/**
* This function is only meant for the last outgoing harvest cycle of a page. It trickles down to using sendBeacon, which should not be used
* to send payloads while the page is still active, due to limitations on how much data can be buffered in the API at any one time.
*/
unload () {
if (this.aborted) return
// If opts.onUnload is defined, these are special actions to execute before attempting to send the final payload.
Expand Down Expand Up @@ -118,7 +125,7 @@ export class HarvestScheduler extends SharedContext {
payload,
opts,
submitMethod,
cbFinished: onHarvestFinished,
cbFinished: cbRanAfterSend,
customUrl: this.opts.customUrl,
raw: this.opts.raw
})
Expand All @@ -129,9 +136,13 @@ export class HarvestScheduler extends SharedContext {
}
return

function onHarvestFinished (result) {
if (result.blocked) scheduler.onHarvestBlocked(opts, result)
else scheduler.onHarvestFinished(opts, result)
/**
* This is executed immediately after harvest sends the data via XHR, or if there's nothing to send. Note that this excludes on unloading / sendBeacon.
* @param {Object} result
*/
function cbRanAfterSend (result) {
if (opts?.forceNoRetry) result.retry = false // discard unsent data rather than re-queuing for next harvest attempt
scheduler.onHarvestFinished(opts, result)
}
}

Expand Down
25 changes: 14 additions & 11 deletions src/common/session/session-entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,29 @@ import { ee } from '../event-emitter/contextual-ee'
import { Timer } from '../timer/timer'
import { isBrowserScope } from '../util/global-scope'
import { DEFAULT_EXPIRES_MS, DEFAULT_INACTIVE_MS, PREFIX } from './constants'
import { LocalMemory } from '../storage/local-memory'
import { InteractionTimer } from '../timer/interaction-timer'
import { wrapEvents } from '../wrap'
import { getModeledObject } from '../config/state/configurable'
import { handle } from '../event-emitter/handle'
import { SUPPORTABILITY_METRIC_CHANNEL } from '../../features/metrics/constants'
import { FEATURE_NAMES } from '../../loaders/features/features'

export const MODE = {
OFF: 0,
FULL: 1,
ERROR: 2
}
// this is what can be stored in local storage (not enforced but probably should be)
// these values should sync between local storage and the parent class props
const model = {
value: '',
inactiveAt: 0,
expiresAt: 0,
updatedAt: Date.now(),
sessionReplay: 0,
sessionTraceActive: false,
sessionReplay: MODE.OFF,
sessionTraceMode: MODE.OFF,
custom: {}
}

export const SESSION_EVENTS = {
PAUSE: 'session-pause',
RESET: 'session-reset',
Expand All @@ -42,16 +45,16 @@ export class SessionEntity {
this.setup(opts)
}

setup ({ agentIdentifier, key, value = generateRandomHexString(16), expiresMs = DEFAULT_EXPIRES_MS, inactiveMs = DEFAULT_INACTIVE_MS, storageAPI = new LocalMemory() }) {
if (!agentIdentifier || !key) throw new Error('Missing Required Fields')
if (!isBrowserScope) this.storage = new LocalMemory()
else this.storage = storageAPI

setup ({ agentIdentifier, key, storage, value = generateRandomHexString(16), expiresMs = DEFAULT_EXPIRES_MS, inactiveMs = DEFAULT_INACTIVE_MS }) {
if (!agentIdentifier || !key || !storage) {
throw new Error(`Missing required field(s):${!agentIdentifier ? ' agentID' : ''}${!key ? ' key' : ''}${!storage ? ' storage' : ''}`)
}
this.agentIdentifier = agentIdentifier
this.storage = storage
this.state = {}

this.sync(model)

this.agentIdentifier = agentIdentifier
// key is intended to act as the k=v pair
this.key = key
// value is intended to act as the primary value of the k=v pair
Expand Down Expand Up @@ -207,7 +210,7 @@ export class SessionEntity {
this.setup({
agentIdentifier: this.agentIdentifier,
key: this.key,
storageAPI: this.storage,
storage: this.storage,
expiresMs: this.expiresMs,
inactiveMs: this.inactiveMs
})
Expand Down
Loading

0 comments on commit dbd995a

Please sign in to comment.