Skip to content

Commit e60f625

Browse files
Flush keepalive batches past keepalive limit (#1100)
1 parent c0c3a82 commit e60f625

File tree

3 files changed

+45
-2
lines changed

3 files changed

+45
-2
lines changed

.changeset/shaggy-dryers-press.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@segment/analytics-next': minor
3+
---
4+
5+
Flush large keepalive requests

packages/browser/src/plugins/segmentio/__tests__/batched-dispatcher.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,31 @@ describe('Batching', () => {
130130
expect(fetch).toHaveBeenCalledTimes(1)
131131
})
132132

133+
it('sends requests if the size of events exceeds keepalive limits', async () => {
134+
const { dispatch } = batch(`https://api.segment.io`, {
135+
size: 600,
136+
keepalive: true,
137+
})
138+
139+
// fatEvent is about ~1kb in size
140+
for (let i = 0; i < 250; i++) {
141+
await dispatch(`https://api.segment.io/v1/t`, {
142+
event: 'small event',
143+
})
144+
}
145+
expect(fetch).not.toHaveBeenCalled()
146+
147+
for (let i = 0; i < 65; i++) {
148+
await dispatch(`https://api.segment.io/v1/t`, {
149+
event: 'fat event',
150+
properties: fatEvent,
151+
})
152+
}
153+
154+
// still called, even though our batch limit is 600 events
155+
expect(fetch).toHaveBeenCalledTimes(1)
156+
})
157+
133158
it('sends requests when the timeout expires', async () => {
134159
const { dispatch } = batch(`https://api.segment.io`, {
135160
size: 100,

packages/browser/src/plugins/segmentio/batched-dispatcher.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ import { onPageChange } from '../../lib/on-page-change'
55
export type BatchingDispatchConfig = {
66
size?: number
77
timeout?: number
8+
keepalive?: boolean
89
}
910

1011
const MAX_PAYLOAD_SIZE = 500
12+
const MAX_KEEPALIVE_SIZE = 64
1113

1214
function kilobytes(buffer: unknown): number {
1315
const size = encodeURI(JSON.stringify(buffer)).split(/%..|./).length - 1
@@ -23,6 +25,15 @@ function approachingTrackingAPILimit(buffer: unknown): boolean {
2325
return kilobytes(buffer) >= MAX_PAYLOAD_SIZE - 50
2426
}
2527

28+
/**
29+
* Checks if payload is over or approaching the limit for keepalive
30+
* requests. If keepalive is enabled we want to avoid
31+
* going over this to prevent data loss.
32+
*/
33+
function passedKeepaliveLimit(buffer: unknown): boolean {
34+
return kilobytes(buffer) >= MAX_KEEPALIVE_SIZE - 10
35+
}
36+
2637
function chunks(batch: object[]): Array<object[]> {
2738
const result: object[][] = []
2839
let index = 0
@@ -67,7 +78,7 @@ export default function batch(
6778
})
6879

6980
return fetch(`https://${apiHost}/b`, {
70-
keepalive: pageUnloaded,
81+
keepalive: config?.keepalive || pageUnloaded,
7182
headers: {
7283
'Content-Type': 'text/plain',
7384
},
@@ -114,7 +125,9 @@ export default function batch(
114125
buffer.push(body)
115126

116127
const bufferOverflow =
117-
buffer.length >= limit || approachingTrackingAPILimit(buffer)
128+
buffer.length >= limit ||
129+
approachingTrackingAPILimit(buffer) ||
130+
(config?.keepalive && passedKeepaliveLimit(buffer))
118131

119132
return bufferOverflow || pageUnloaded ? flush() : scheduleFlush()
120133
}

0 commit comments

Comments
 (0)