Skip to content

Commit 603ea1d

Browse files
committed
added eventUploadPeriodMillis functionality
1 parent c8aaa6b commit 603ea1d

File tree

5 files changed

+124
-38
lines changed

5 files changed

+124
-38
lines changed

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,9 @@ You can configure Amplitude by passing an object as the third argument to the `i
8787
// optional configuration options
8888
saveEvents: true,
8989
includeUtm: true,
90-
includeReferrer: true
90+
includeReferrer: true,
91+
batchEvents: true,
92+
eventUploadThreshold: 50
9193
})
9294

9395
| option | description | default |
@@ -97,8 +99,9 @@ You can configure Amplitude by passing an object as the third argument to the `i
9799
| uploadBatchSize | Maximum number of events to send to the server per request. | 100 |
98100
| includeUtm | If `true`, finds utm parameters in the query string or the __utmz cookie, parses, and includes them as user propeties on all events uploaded. | `false` |
99101
| includeReferrer | If `true`, includes `referrer` and `referring_domain` as user propeties on all events uploaded. | `false` |
100-
| batchEvents | If `true`, events are uploaded only when the number of unsent events is greater than `eventUploadThreshold`. | `false` |
101-
| eventUploadThreshold | Minimum number of events to send to the server per request if `batchEvents` is `true`. | 30 |
102+
| batchEvents | If `true`, events are batched together and uploaded only when the number of unsent events is greater than or equal to `eventUploadThreshold` or after `eventUploadPeriodMillis` milliseconds have passed since the first unsent event was logged. | `false` |
103+
| eventUploadThreshold | Minimum number of events to batch together per request if `batchEvents` is `true`. | 30 |
104+
| eventUploadPeriodMillis | Amount of time in milliseconds that the SDK waits before uploading events if `batchEvents` is `true`. | 30*1000 |
102105

103106

104107
# Advanced #

amplitude.js

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,8 @@ var DEFAULT_OPTIONS = {
141141
unsentKey: 'amplitude_unsent',
142142
uploadBatchSize: 100,
143143
batchEvents: false,
144-
eventUploadThreshold: 30
144+
eventUploadThreshold: 30,
145+
eventUploadPeriodMillis: 30 * 1000 // 30s
145146
};
146147
var LocalStorageKeys = {
147148
LAST_EVENT_ID: 'amplitude_lastEventId',
@@ -199,6 +200,7 @@ Amplitude.prototype.init = function(apiKey, opt_userId, opt_config) {
199200
this.options.uploadBatchSize = opt_config.uploadBatchSize || this.options.uploadBatchSize;
200201
this.options.eventUploadThreshold = opt_config.eventUploadThreshold || this.options.eventUploadThreshold;
201202
this.options.savedMaxCount = opt_config.savedMaxCount || this.options.savedMaxCount;
203+
this.options.eventUploadPeriodMillis = opt_config.eventUploadPeriodMillis || this.options.eventUploadPeriodMillis;
202204
}
203205

204206
Cookie.options({
@@ -228,9 +230,8 @@ Amplitude.prototype.init = function(apiKey, opt_userId, opt_config) {
228230
}
229231
}
230232
}
231-
if (this.shouldSendEvents()) {
232-
this.sendEvents();
233-
}
233+
234+
this._sendEventsIfReady();
234235

235236
if (this.options.includeUtm) {
236237
this._initUtmData();
@@ -261,11 +262,21 @@ Amplitude.prototype.nextEventId = function() {
261262
return this._eventId;
262263
};
263264

264-
Amplitude.prototype.shouldSendEvents = function() {
265-
var batchEvents = this.options.batchEvents;
266-
var numEvents = this._unsentEvents.length;
267-
var threshold = this.options.eventUploadThreshold;
268-
return (!batchEvents && numEvents > 0) || (batchEvents && numEvents >= threshold);
265+
Amplitude.prototype._sendEventsIfReady = function() {
266+
if (this._unsentEvents.length === 0) {
267+
return;
268+
}
269+
270+
if (!this.options.batchEvents) {
271+
this.sendEvents();
272+
return;
273+
}
274+
275+
if (this._unsentEvents.length >= this.options.eventUploadThreshold) {
276+
this.sendEvents();
277+
} else {
278+
setTimeout(this.sendEvents.bind(this), this.options.eventUploadPeriodMillis);
279+
}
269280
};
270281

271282
var _loadCookieData = function(scope) {
@@ -490,9 +501,7 @@ Amplitude.prototype._logEvent = function(eventType, eventProperties, apiProperti
490501
this.saveEvents();
491502
}
492503

493-
if (this.shouldSendEvents()){
494-
this.sendEvents();
495-
}
504+
this._sendEventsIfReady();
496505

497506
return eventId;
498507
} catch (e) {
@@ -539,7 +548,7 @@ Amplitude.prototype.removeEvents = function (maxEventId) {
539548
};
540549

541550
Amplitude.prototype.sendEvents = function() {
542-
if (!this._sending && !this.options.optOut) {
551+
if (!this._sending && !this.options.optOut && this._unsentEvents.length > 0) {
543552
this._sending = true;
544553
var url = ('https:' === window.location.protocol ? 'https' : 'http') + '://' +
545554
this.options.apiEndpoint + '/';
@@ -572,9 +581,8 @@ Amplitude.prototype.sendEvents = function() {
572581
}
573582

574583
// Send more events if any queued during previous send.
575-
if (scope.shouldSendEvents()) {
576-
scope.sendEvents();
577-
}
584+
scope._sendEventsIfReady();
585+
578586
} else if (status === 413) {
579587
//log('request too large');
580588
// Can't even get this one massive event through. Drop it.

amplitude.min.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/amplitude.js

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ var DEFAULT_OPTIONS = {
2929
unsentKey: 'amplitude_unsent',
3030
uploadBatchSize: 100,
3131
batchEvents: false,
32-
eventUploadThreshold: 30
32+
eventUploadThreshold: 30,
33+
eventUploadPeriodMillis: 30 * 1000 // 30s
3334
};
3435
var LocalStorageKeys = {
3536
LAST_EVENT_ID: 'amplitude_lastEventId',
@@ -87,6 +88,7 @@ Amplitude.prototype.init = function(apiKey, opt_userId, opt_config) {
8788
this.options.uploadBatchSize = opt_config.uploadBatchSize || this.options.uploadBatchSize;
8889
this.options.eventUploadThreshold = opt_config.eventUploadThreshold || this.options.eventUploadThreshold;
8990
this.options.savedMaxCount = opt_config.savedMaxCount || this.options.savedMaxCount;
91+
this.options.eventUploadPeriodMillis = opt_config.eventUploadPeriodMillis || this.options.eventUploadPeriodMillis;
9092
}
9193

9294
Cookie.options({
@@ -116,9 +118,8 @@ Amplitude.prototype.init = function(apiKey, opt_userId, opt_config) {
116118
}
117119
}
118120
}
119-
if (this.shouldSendEvents()) {
120-
this.sendEvents();
121-
}
121+
122+
this._sendEventsIfReady();
122123

123124
if (this.options.includeUtm) {
124125
this._initUtmData();
@@ -149,11 +150,21 @@ Amplitude.prototype.nextEventId = function() {
149150
return this._eventId;
150151
};
151152

152-
Amplitude.prototype.shouldSendEvents = function() {
153-
var batchEvents = this.options.batchEvents;
154-
var numEvents = this._unsentEvents.length;
155-
var threshold = this.options.eventUploadThreshold;
156-
return (!batchEvents && numEvents > 0) || (batchEvents && numEvents >= threshold);
153+
Amplitude.prototype._sendEventsIfReady = function() {
154+
if (this._unsentEvents.length === 0) {
155+
return;
156+
}
157+
158+
if (!this.options.batchEvents) {
159+
this.sendEvents();
160+
return;
161+
}
162+
163+
if (this._unsentEvents.length >= this.options.eventUploadThreshold) {
164+
this.sendEvents();
165+
} else {
166+
setTimeout(this.sendEvents.bind(this), this.options.eventUploadPeriodMillis);
167+
}
157168
};
158169

159170
var _loadCookieData = function(scope) {
@@ -378,9 +389,7 @@ Amplitude.prototype._logEvent = function(eventType, eventProperties, apiProperti
378389
this.saveEvents();
379390
}
380391

381-
if (this.shouldSendEvents()){
382-
this.sendEvents();
383-
}
392+
this._sendEventsIfReady();
384393

385394
return eventId;
386395
} catch (e) {
@@ -427,7 +436,7 @@ Amplitude.prototype.removeEvents = function (maxEventId) {
427436
};
428437

429438
Amplitude.prototype.sendEvents = function() {
430-
if (!this._sending && !this.options.optOut) {
439+
if (!this._sending && !this.options.optOut && this._unsentEvents.length > 0) {
431440
this._sending = true;
432441
var url = ('https:' === window.location.protocol ? 'https' : 'http') + '://' +
433442
this.options.apiEndpoint + '/';
@@ -460,9 +469,8 @@ Amplitude.prototype.sendEvents = function() {
460469
}
461470

462471
// Send more events if any queued during previous send.
463-
if (scope.shouldSendEvents()) {
464-
scope.sendEvents();
465-
}
472+
scope._sendEventsIfReady();
473+
466474
} else if (status === 413) {
467475
//log('request too large');
468476
// Can't even get this one massive event through. Drop it.

test/amplitude.js

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,16 @@ describe('Amplitude', function() {
130130

131131
describe('logEvent', function() {
132132

133+
var clock;
134+
133135
beforeEach(function() {
136+
clock = sinon.useFakeTimers();
134137
amplitude.init(apiKey);
135138
});
136139

137140
afterEach(function() {
138141
reset();
142+
clock.restore();
139143
});
140144

141145
it('should send request', function() {
@@ -288,7 +292,12 @@ describe('Amplitude', function() {
288292
});
289293

290294
it('should batch events sent', function() {
291-
amplitude.init(apiKey, null, {batchEvents: true, eventUploadThreshold: 10});
295+
var eventUploadPeriodMillis = 10*1000;
296+
amplitude.init(apiKey, null, {
297+
batchEvents: true,
298+
eventUploadThreshold: 10,
299+
eventUploadPeriodMillis: eventUploadPeriodMillis
300+
});
292301

293302
for (var i = 0; i < 15; i++) {
294303
amplitude.logEvent('Event', {index: i});
@@ -307,8 +316,66 @@ describe('Amplitude', function() {
307316
var unsentEvents = amplitude._unsentEvents;
308317
assert.lengthOf(unsentEvents, 5);
309318
assert.deepEqual(unsentEvents[4].event_properties, {index: 14});
319+
320+
// remaining 5 events should be sent by the delayed sendEvent call
321+
clock.tick(eventUploadPeriodMillis);
322+
assert.lengthOf(server.requests, 2);
323+
server.respondWith('success');
324+
server.respond();
325+
assert.lengthOf(amplitude._unsentEvents, 0);
326+
var events = JSON.parse(querystring.parse(server.requests[1].requestBody).e);
327+
assert.lengthOf(events, 5);
328+
assert.deepEqual(events[4].event_properties, {index: 14});
310329
});
311330

331+
it('should send events after a delay', function() {
332+
var eventUploadPeriodMillis = 10*1000;
333+
amplitude.init(apiKey, null, {
334+
batchEvents: true,
335+
eventUploadThreshold: 2,
336+
eventUploadPeriodMillis: eventUploadPeriodMillis
337+
});
338+
amplitude.logEvent('Event');
339+
340+
// saveEvent should not have been called yet
341+
assert.lengthOf(amplitude._unsentEvents, 1);
342+
assert.lengthOf(server.requests, 0);
343+
344+
// saveEvent should be called after delay
345+
clock.tick(eventUploadPeriodMillis);
346+
assert.lengthOf(server.requests, 1);
347+
server.respondWith('success');
348+
server.respond();
349+
var events = JSON.parse(querystring.parse(server.requests[0].requestBody).e);
350+
assert.lengthOf(events, 1);
351+
assert.deepEqual(events[0].event_type, 'Event');
352+
});
353+
354+
it('should not send events after a delay if no events to send', function() {
355+
var eventUploadPeriodMillis = 10*1000;
356+
amplitude.init(apiKey, null, {
357+
batchEvents: true,
358+
eventUploadThreshold: 2,
359+
eventUploadPeriodMillis: eventUploadPeriodMillis
360+
});
361+
amplitude.logEvent('Event1');
362+
amplitude.logEvent('Event2');
363+
364+
// saveEvent triggered by 2 event batch threshold
365+
assert.lengthOf(amplitude._unsentEvents, 2);
366+
assert.lengthOf(server.requests, 1);
367+
server.respondWith('success');
368+
server.respond();
369+
var events = JSON.parse(querystring.parse(server.requests[0].requestBody).e);
370+
assert.lengthOf(events, 2);
371+
assert.deepEqual(events[1].event_type, 'Event2');
372+
373+
// saveEvent should be called after delay, but no request made
374+
assert.lengthOf(amplitude._unsentEvents, 0);
375+
clock.tick(eventUploadPeriodMillis);
376+
assert.lengthOf(server.requests, 1);
377+
})
378+
312379
it('should back off on 413 status', function() {
313380
amplitude.init(apiKey, null, {uploadBatchSize: 10});
314381

0 commit comments

Comments
 (0)