Skip to content

Commit 14aff3d

Browse files
committed
feat: buffer data plane events conditionally
1 parent 429954a commit 14aff3d

2 files changed

Lines changed: 84 additions & 53 deletions

File tree

packages/analytics-js/src/components/core/Analytics.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,8 @@ class Analytics implements IAnalytics {
303303
}
304304
}
305305

306+
this.setActiveDestinations();
307+
306308
// Initialize event manager
307309
this.eventManager?.init();
308310

@@ -387,6 +389,16 @@ class Analytics implements IAnalytics {
387389
}
388390
}
389391

392+
private setActiveDestinations() {
393+
this.pluginsManager?.invokeSingle(
394+
'nativeDestinations.setActiveDestinations',
395+
state,
396+
this.pluginsManager,
397+
this.errorHandler,
398+
this.logger,
399+
);
400+
}
401+
390402
/**
391403
* Load device mode destinations
392404
*/
@@ -400,13 +412,7 @@ class Analytics implements IAnalytics {
400412
}
401413

402414
// Set in state the desired activeDestinations to inject in DOM
403-
this.pluginsManager?.invokeSingle(
404-
'nativeDestinations.setActiveDestinations',
405-
state,
406-
this.pluginsManager,
407-
this.errorHandler,
408-
this.logger,
409-
);
415+
this.setActiveDestinations();
410416

411417
const totalDestinationsToLoad = state.nativeDestinations.activeDestinations.value.length;
412418
if (totalDestinationsToLoad === 0) {

packages/analytics-js/src/components/eventRepository/EventRepository.ts

Lines changed: 71 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ class EventRepository implements IEventRepository {
3232
dataplaneEventsQueue: any;
3333
destinationsEventsQueue: any;
3434
dmtEventsQueue: any;
35+
eventsBuffer: RudderEvent[];
36+
isEventBufferingActive: boolean;
3537

3638
/**
3739
*
@@ -52,6 +54,8 @@ class EventRepository implements IEventRepository {
5254
this.httpClient = httpClient;
5355
this.logger = logger;
5456
this.storeManager = storeManager;
57+
this.eventsBuffer = [];
58+
this.isEventBufferingActive = true;
5559
}
5660

5761
/**
@@ -96,50 +100,66 @@ class EventRepository implements IEventRepository {
96100
});
97101

98102
const bufferEventsBeforeConsent = shouldBufferEventsForPreConsent(state);
103+
if (!bufferEventsBeforeConsent) {
104+
this.startDpEventsQueue();
105+
}
106+
}
99107

100-
// Start the queue processing only when the destinations are ready or hybrid mode destinations exist
101-
// However, events will be enqueued for now.
102-
// At the time of processing the events, the integrations config data from destinations
103-
// is merged into the event object
104-
let timeoutId: number;
105-
effect(() => {
106-
const shouldBufferDpEvents =
107-
state.loadOptions.value.bufferDataPlaneEventsUntilReady === true &&
108-
state.nativeDestinations.clientDestinationsReady.value === false;
108+
private startDpEventsQueue() {
109+
const bufferEventsUntilReady = state.loadOptions.value
110+
.bufferDataPlaneEventsUntilReady as boolean;
109111

110-
const hybridDestExist = state.nativeDestinations.activeDestinations.value.some(
111-
(dest: Destination) => isHybridModeDestination(dest),
112-
);
112+
const hybridDestExist = state.nativeDestinations.activeDestinations.value.some(
113+
(dest: Destination) => isHybridModeDestination(dest),
114+
);
115+
const shouldBufferEvents = bufferEventsUntilReady && hybridDestExist;
116+
117+
// Start the data plane events queue and replay the events from the buffer
118+
// This function is called when the client destinations are ready
119+
// or when the timeout expires
120+
// or when no buffering is required
121+
const startDpQueueAndReplayEvents = () => {
122+
this.isEventBufferingActive = false;
123+
this.eventsBuffer.forEach(event => {
124+
this.enqueue(event);
125+
});
126+
127+
if (this.dataplaneEventsQueue?.scheduleTimeoutActive !== true) {
128+
this.dataplaneEventsQueue?.start();
129+
}
130+
131+
this.eventsBuffer = [];
132+
};
113133

114-
if (
115-
(hybridDestExist === false || shouldBufferDpEvents === false) &&
116-
!bufferEventsBeforeConsent &&
117-
this.dataplaneEventsQueue?.scheduleTimeoutActive !== true
118-
) {
134+
let timeoutId: number;
135+
// Start the queue when no event buffering is required
136+
// or when buffering is required and the client destinations are ready
137+
effect(() => {
138+
if (!shouldBufferEvents || state.nativeDestinations.clientDestinationsReady.value) {
119139
(globalThis as typeof window).clearTimeout(timeoutId);
120-
this.dataplaneEventsQueue?.start();
140+
startDpQueueAndReplayEvents();
121141
}
122142
});
123143

124144
// Force start the data plane events queue processing after a timeout
125-
if (state.loadOptions.value.bufferDataPlaneEventsUntilReady === true) {
145+
if (shouldBufferEvents) {
146+
this.isEventBufferingActive = true;
126147
timeoutId = (globalThis as typeof window).setTimeout(() => {
127-
if (this.dataplaneEventsQueue?.scheduleTimeoutActive !== true) {
128-
this.dataplaneEventsQueue?.start();
129-
}
148+
startDpQueueAndReplayEvents();
130149
}, state.loadOptions.value.dataPlaneEventsBufferTimeout);
131150
}
132151
}
133152

134153
resume() {
135-
if (this.dataplaneEventsQueue?.scheduleTimeoutActive !== true) {
136-
if (state.consents.postConsent.value.discardPreConsentEvents) {
137-
this.dataplaneEventsQueue?.clear();
138-
this.destinationsEventsQueue?.clear();
139-
}
140-
141-
this.dataplaneEventsQueue?.start();
154+
if (
155+
this.dataplaneEventsQueue?.scheduleTimeoutActive !== true &&
156+
state.consents.postConsent.value.discardPreConsentEvents
157+
) {
158+
this.dataplaneEventsQueue?.clear();
159+
this.destinationsEventsQueue?.clear();
142160
}
161+
162+
this.startDpEventsQueue();
143163
}
144164

145165
/**
@@ -149,24 +169,29 @@ class EventRepository implements IEventRepository {
149169
*/
150170
enqueue(event: RudderEvent, callback?: ApiCallback): void {
151171
const dpQEvent = getFinalEvent(event, state);
152-
this.pluginsManager.invokeSingle(
153-
`${DATA_PLANE_QUEUE_EXT_POINT_PREFIX}.enqueue`,
154-
state,
155-
this.dataplaneEventsQueue,
156-
dpQEvent,
157-
this.errorHandler,
158-
this.logger,
159-
);
160172

161-
const dQEvent = clone(event);
162-
this.pluginsManager.invokeSingle(
163-
`${DESTINATIONS_QUEUE_EXT_POINT_PREFIX}.enqueue`,
164-
state,
165-
this.destinationsEventsQueue,
166-
dQEvent,
167-
this.errorHandler,
168-
this.logger,
169-
);
173+
if (this.isEventBufferingActive) {
174+
this.eventsBuffer.push(dpQEvent);
175+
} else {
176+
this.pluginsManager.invokeSingle(
177+
`${DATA_PLANE_QUEUE_EXT_POINT_PREFIX}.enqueue`,
178+
state,
179+
this.dataplaneEventsQueue,
180+
dpQEvent,
181+
this.errorHandler,
182+
this.logger,
183+
);
184+
185+
const dQEvent = clone(event);
186+
this.pluginsManager.invokeSingle(
187+
`${DESTINATIONS_QUEUE_EXT_POINT_PREFIX}.enqueue`,
188+
state,
189+
this.destinationsEventsQueue,
190+
dQEvent,
191+
this.errorHandler,
192+
this.logger,
193+
);
194+
}
170195

171196
// Invoke the callback if it exists
172197
const apiName = `${event.type.charAt(0).toUpperCase()}${event.type.slice(1)}${API_SUFFIX}`;

0 commit comments

Comments
 (0)