diff --git a/package.json b/package.json index 0cbfb2d0..bf215b75 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "@types/express": "^4.11.1", "@types/knex": "^0.14.14", "@types/lodash": "^4.14.109", + "@types/memoizee": "^0.4.2", "@types/mz": "0.0.32", "@types/node": "^10.3.1", "@types/rwlock": "^5.0.2", diff --git a/src/event-tracker.ts b/src/event-tracker.ts index 1647ac02..12796442 100644 --- a/src/event-tracker.ts +++ b/src/event-tracker.ts @@ -1,6 +1,7 @@ import * as Bluebird from 'bluebird'; import mask = require('json-mask'); import * as _ from 'lodash'; +import * as memoizee from 'memoizee'; import Mixpanel = require('mixpanel'); @@ -13,6 +14,10 @@ interface InitArgs { mixpanelToken: string; } +// The minimum amount of time to wait between sending +// events of the same type +const eventDebounceTime = 60000; + const mixpanelMask = [ 'appId', 'delay', @@ -78,9 +83,16 @@ export class EventTracker { } properties = this.assignDefaultProperties(properties); - this.client.track(event, properties); + this.debouncedLogger(event)(properties); } + private debouncedLogger = memoizee((event: string) => { + // Call this function at maximum once every minute + return _.debounce((properties) => { + this.client.track(event, properties); + }, eventDebounceTime, { leading: true }); + }, { primitive: true }); + private logEvent(...args: string[]) { console.log(...args); } diff --git a/test/09-event-tracker.spec.coffee b/test/09-event-tracker.spec.coffee index caabd7d3..8fff9066 100644 --- a/test/09-event-tracker.spec.coffee +++ b/test/09-event-tracker.spec.coffee @@ -77,3 +77,39 @@ describe 'EventTracker', -> uuid: 'barbaz' distinct_id: 'barbaz' }) + + describe 'Rate limiting', -> + + it 'should rate limit events of the same type', -> + @eventTracker.client.track.reset() + + @eventTracker.track('test', { }); + @eventTracker.track('test', { }); + @eventTracker.track('test', { }); + @eventTracker.track('test', { }); + @eventTracker.track('test', { }); + + expect(@eventTracker.client.track).to.have.callCount(1) + + it 'should rate limit events of the same type with different arguments', -> + @eventTracker.client.track.reset() + + @eventTracker.track('test2', { a: 1 }); + @eventTracker.track('test2', { b: 2 }); + @eventTracker.track('test2', { c: 3 }); + @eventTracker.track('test2', { d: 4 }); + @eventTracker.track('test2', { e: 5 }); + + expect(@eventTracker.client.track).to.have.callCount(1) + + it 'should not rate limit events of different types', -> + @eventTracker.client.track.reset() + + @eventTracker.track('test3', { a: 1 }); + @eventTracker.track('test4', { b: 2 }); + @eventTracker.track('test5', { c: 3 }); + @eventTracker.track('test6', { d: 4 }); + @eventTracker.track('test7', { e: 5 }); + + expect(@eventTracker.client.track).to.have.callCount(5) +