Skip to content

Commit 56e75b2

Browse files
fix: Ensure event buffer always exists (#1452)
1 parent fd5a8a9 commit 56e75b2

File tree

5 files changed

+33
-25
lines changed

5 files changed

+33
-25
lines changed

src/features/soft_navigations/aggregate/index.js

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,7 @@ export class Aggregate extends AggregateBase {
2626
if (agentRef.runtime.session?.isNew) this.initialPageLoadInteraction.customAttributes.isFirstOfSession = true // mark the hard page load as first of its session
2727
this.initialPageLoadInteraction.forceSave = true // unless forcibly ignored, iPL always finish by default
2828
const ixn = this.initialPageLoadInteraction
29-
/** this.events (ixns to harvest) has already been set up, use it immediately */
30-
if (this.interactionsToHarvest) this.interactionsToHarvest.add(ixn)
31-
else {
32-
/** this.events (ixns to harvest) hasnt been initialized yet... wait for it */
33-
this.ee.on('entity-added', () => {
34-
this.interactionsToHarvest = this.events
35-
this.interactionsToHarvest.add(ixn)
36-
})
37-
}
29+
this.interactionsToHarvest.add(ixn)
3830
this.initialPageLoadInteraction = null
3931
})
4032
timeToFirstByte.subscribe(({ attrs }) => {

src/features/utils/aggregate-base.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,13 @@ export class AggregateBase extends FeatureBase {
3434
this.harvestOpts = {} // features aggregate classes can define custom opts for when their harvest is called
3535

3636
const agentEntityGuid = this.agentRef?.runtime?.appMetadata?.agents?.[0]?.entityGuid
37-
if (agentEntityGuid) {
38-
this.#setupEventStore(agentEntityGuid) // if there's no entity guid, wont dont anything, and will wait for rum flags
39-
} else {
37+
this.#setupEventStore(agentEntityGuid)
38+
if (!agentEntityGuid) {
39+
/** wait for the entity guid from the rum response and use to it to further configure things to set the default entity to share an indexed entity with entityGuid */
4040
this.ee.on('entity-added', entity => {
41-
this.#setupEventStore(entity.entityGuid)
41+
// not all event managers have this fn, like ST and SR
42+
// this allows the lookup to work for the default and an entityGuid without creating two separate buffers
43+
this.events?.setEventStore?.(entity.entityGuid)
4244
})
4345
}
4446
}
@@ -49,7 +51,7 @@ export class AggregateBase extends FeatureBase {
4951
* @returns {void}
5052
*/
5153
#setupEventStore (entityGuid) {
52-
if (this.events || !entityGuid) return
54+
if (this.events) return
5355
switch (this.featureName) {
5456
// SessionTrace + Replay have their own storage mechanisms.
5557
case FEATURE_NAMES.sessionTrace:

src/features/utils/event-store-manager.js

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
*/
55
import { dispatchGlobalEvent } from '../../common/dispatch/global-event'
66
import { activatedFeatures } from '../../common/util/feature-flags'
7+
import { isContainerAgentTarget } from '../../common/util/target'
78

9+
const DEFAULT_KEY = 'NR_CONTAINER_AGENT' // this is the default entity guid used for the default storage instance
810
/**
911
* This layer allows multiple browser entity apps, or "target", to each have their own segregated storage instance.
1012
* The purpose is so the harvester can send data to different apps within the same agent. Each feature should have a manager if it needs this capability.
@@ -13,27 +15,38 @@ export class EventStoreManager {
1315
/**
1416
* @param {object} agentRef - reference to base agent class
1517
* @param {EventBuffer|EventAggregator} storageClass - the type of storage to use in this manager; 'EventBuffer' (1), 'EventAggregator' (2)
18+
* @param {string} [defaultEntityGuid] - the entity guid to use as the default storage instance; if not provided, a new one is created
19+
* @param {string} featureName - the name of the feature this manager is for; used for event dispatching
1620
*/
1721
constructor (agentRef, storageClass, defaultEntityGuid, featureName) {
1822
this.agentRef = agentRef
1923
this.entityManager = agentRef.runtime.entityManager
2024
this.StorageClass = storageClass
21-
this.appStorageMap = new Map()
22-
this.defaultEntity = this.#getEventStore(defaultEntityGuid)
25+
this.appStorageMap = new Map([[DEFAULT_KEY, new this.StorageClass()]])
2326
this.featureName = featureName
27+
this.setEventStore(defaultEntityGuid)
2428
}
2529

2630
/**
2731
* Always returns a storage instance. Creates one if one does not exist. If a lookup is not provided, uses the DEFAULT namespace
2832
* @param {string=} targetEntityGuid the lookup
2933
* @returns {*} ALWAYS returns a storage instance
3034
*/
31-
#getEventStore (targetEntityGuid) {
32-
if (!targetEntityGuid) return this.defaultEntity
33-
if (!this.appStorageMap.has(targetEntityGuid)) this.appStorageMap.set(targetEntityGuid, new this.StorageClass())
35+
#getEventStore (targetEntityGuid = DEFAULT_KEY) {
36+
if (!this.appStorageMap.has(targetEntityGuid)) this.setEventStore(targetEntityGuid)
3437
return this.appStorageMap.get(targetEntityGuid)
3538
}
3639

40+
setEventStore (targetEntityGuid) {
41+
/** we should already have an event store for the default */
42+
if (!targetEntityGuid) return
43+
/** if the target is the container agent, SHARE the default storage -- otherwise create a new event store */
44+
const eventStorage = (isContainerAgentTarget(this.entityManager.get(targetEntityGuid), this.agentRef))
45+
? this.appStorageMap.get(DEFAULT_KEY)
46+
: new this.StorageClass()
47+
this.appStorageMap.set(targetEntityGuid, eventStorage)
48+
}
49+
3750
// This class must contain an union of all methods from all supported storage classes and conceptualize away the target app argument.
3851

3952
/**

tests/components/ajax/aggregate.test.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,10 @@ describe('prepareHarvest', () => {
166166
})
167167
})
168168

169-
test('correctly exits if maxPayload is too small', () => {
170-
ajaxAggregate.events.defaultEntity.maxPayloadSize = 10 // this is too small for any AJAX payload to fit in
171-
for (let callNo = 0; callNo < 10; callNo++) ajaxAggregate.ee.emit('xhr', ajaxArguments, context)
169+
test('correctly exits if maxPayload is too small', async () => {
170+
for (let callNo = 0; callNo < 10; callNo++) {
171+
ajaxAggregate.ee.emit('xhr', [{ ...ajaxArguments[0], pathname: 'x'.repeat(1000000) }, ...ajaxArguments], context)
172+
}
172173

173174
const serializedPayload = ajaxAggregate.makeHarvestPayload(false)
174175
expect(serializedPayload).toBeUndefined() // payload that are each too small for limit will be dropped

tests/unit/features/utils/event-store-manager.test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const entity = { entityGuid, ...info }
99
const entityManager = new EntityManager({ info, ee: { emit: jest.fn() } })
1010
entityManager.setDefaultEntity(entity)
1111
entityManager.set(entityGuid, entity)
12-
const mockAgentRef = { runtime: { entityManager, appMetadata: { agents: [{ entityGuid }] } } }
12+
const mockAgentRef = { info, runtime: { entityManager, appMetadata: { agents: [{ entityGuid }] } } }
1313
describe('EventStoreManager', () => {
1414
test('uses EventBuffer class when storageChoice is EventBuffer', () => {
1515
const store = new EventStoreManager(mockAgentRef, EventBuffer, mockAgentRef.runtime.appMetadata.agents[0].entityGuid, 'test')
@@ -95,15 +95,15 @@ describe('EventStoreManager', () => {
9595
})
9696

9797
test('isEmpty checks ALL storages when target is not provided', () => {
98-
const tgt1 = { name: 'myTarget' }
98+
const tgt1 = 'myTarget'
9999
const store = new EventStoreManager(mockAgentRef, EventBuffer, mockAgentRef.runtime.appMetadata.agents[0].entityGuid, 'test')
100100
expect(store.isEmpty()).toBeTruthy()
101101
store.add('myEvent', tgt1)
102102
expect(store.isEmpty()).toBeFalsy()
103103
store.clear()
104104
expect(store.isEmpty()).toBeTruthy()
105105

106-
store.add('evt2', { name: 'otherTarget' })
106+
store.add('evt2', 'otherTarget')
107107
expect(store.isEmpty()).toBeFalsy()
108108
expect(store.isEmpty(undefined, tgt1)).toBeTruthy()
109109
store.add('evt1', tgt1)

0 commit comments

Comments
 (0)