Skip to content

Commit

Permalink
feat: make inital metrics interval configurable.
Browse files Browse the repository at this point in the history
  • Loading branch information
ivarconr committed May 6, 2024
1 parent f4299a8 commit dcdf84d
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 3 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ The Unleash SDK takes the following options:
| refreshInterval | no | `30` | How often, in seconds, the SDK should check for updated toggle configuration. If set to 0 will disable checking for updates |
| disableRefresh | no | `false` | If set to true, the client will not check for updated toggle configuration |
| metricsInterval | no | `60` | How often, in seconds, the SDK should send usage metrics back to Unleash Proxy |
| metricsIntervalInitial | no | `2` | How long the SDK should wait for the first metrics report back to the Unleash API. It cannot be longer than the `metricsInterval`. If you want to disable the initial metrics call you can set it to 0. |
| disableMetrics | no | `false` | Set this option to `true` if you want to disable usage metrics |
| storageProvider | no | `LocalStorageProvider` in browser, `InMemoryStorageProvider` otherwise | Allows you to inject a custom storeProvider |
| fetch | no | `window.fetch` or global `fetch` | Allows you to override the fetch implementation to use. Useful in Node.js environments where you can inject `node-fetch` |
Expand Down
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ interface IConfig extends IStaticContext {
disableRefresh?: boolean;
refreshInterval?: number;
metricsInterval?: number;
metricsIntervalInitial?: number;
disableMetrics?: boolean;
storageProvider?: IStorageProvider;
context?: IMutableContext;
Expand Down Expand Up @@ -153,6 +154,7 @@ export class UnleashClient extends TinyEmitter {
disableRefresh = false,
refreshInterval = 30,
metricsInterval = 30,
metricsIntervalInitial = 2,
disableMetrics = false,
appName,
environment = 'default',
Expand Down Expand Up @@ -232,6 +234,7 @@ export class UnleashClient extends TinyEmitter {
fetch,
headerName,
customHeaders,
metricsIntervalInitial,
});
}

Expand Down
54 changes: 54 additions & 0 deletions src/metrics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ test('should be disabled by flag disableMetrics', async () => {
clientKey: '123',
fetch: fetchMock,
headerName: 'Authorization',
metricsIntervalInitial: 0,
});

metrics.count('foo', true);
Expand All @@ -40,6 +41,7 @@ test('should send metrics', async () => {
clientKey: '123',
fetch: fetchMock,
headerName: 'Authorization',
metricsIntervalInitial: 0,
});

metrics.count('foo', true);
Expand Down Expand Up @@ -76,6 +78,7 @@ test('should send metrics with custom auth header', async () => {
clientKey: '123',
fetch: fetchMock,
headerName: 'NotAuthorization',
metricsIntervalInitial: 0,
});

metrics.count('foo', true);
Expand All @@ -99,6 +102,7 @@ test('Should send initial metrics after 2 seconds', () => {
clientKey: '123',
fetch: fetchMock,
headerName: 'Authorization',
metricsIntervalInitial: 2,
});

metrics.start();
Expand All @@ -112,6 +116,54 @@ test('Should send initial metrics after 2 seconds', () => {
expect(fetchMock.mock.calls.length).toEqual(1);
});

test('Should send initial metrics after 5 seconds, when metricsIntervalInitial is higher than metricsInterval', () => {
const metrics = new Metrics({
onError: console.error,
appName: 'test',
metricsInterval: 5,
disableMetrics: false,
url: 'http://localhost:3000',
clientKey: '123',
fetch: fetchMock,
headerName: 'Authorization',
metricsIntervalInitial: 20,
});

metrics.start();

metrics.count('foo', true);
metrics.count('foo', true);
metrics.count('foo', false);
metrics.count('bar', false);
// Account for 2 second timeout before the set interval starts
jest.advanceTimersByTime(5000);
expect(fetchMock.mock.calls.length).toEqual(1);
});

test('Should not send initial metrics if disabled', () => {
const metrics = new Metrics({
onError: console.error,
appName: 'test',
metricsInterval: 5,
disableMetrics: false,
url: 'http://localhost:3000',
clientKey: '123',
fetch: fetchMock,
headerName: 'Authorization',
metricsIntervalInitial: 0,
});

metrics.start();

metrics.count('foo', true);
metrics.count('foo', true);
metrics.count('foo', false);
metrics.count('bar', false);
// Account for 2 second timeout before the set interval starts
jest.advanceTimersByTime(2000);
expect(fetchMock.mock.calls.length).toEqual(0);
});

test('should send metrics based on timer interval', async () => {
const metrics = new Metrics({
onError: console.error,
Expand All @@ -122,6 +174,7 @@ test('should send metrics based on timer interval', async () => {
clientKey: '123',
fetch: fetchMock,
headerName: 'Authorization',
metricsIntervalInitial: 2,
});

metrics.start();
Expand Down Expand Up @@ -162,6 +215,7 @@ describe('Custom headers for metrics', () => {
fetch: fetchMock,
headerName: 'Authorization',
customHeaders,
metricsIntervalInitial: 2,
});

metrics.count('foo', true);
Expand Down
16 changes: 13 additions & 3 deletions src/metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface MetricsOptions {
fetch: any;
headerName: string;
customHeaders?: Record<string, string>;
metricsIntervalInitial: number;
}

interface VariantBucket {
Expand Down Expand Up @@ -51,6 +52,7 @@ export default class Metrics {
private fetch: any;
private headerName: string;
private customHeaders: Record<string, string>;
private metricsIntervalInitial: number;

constructor({
onError,
Expand All @@ -63,18 +65,21 @@ export default class Metrics {
fetch,
headerName,
customHeaders = {},
metricsIntervalInitial,
}: MetricsOptions) {
this.onError = onError;
this.onSent = onSent || doNothing;
this.disabled = disableMetrics;
this.metricsInterval = metricsInterval * 1000;
this.metricsIntervalInitial = Math.min(metricsInterval, metricsIntervalInitial) * 1000;
this.appName = appName;
this.url = url instanceof URL ? url : new URL(url);
this.clientKey = clientKey;
this.bucket = this.createEmptyBucket();
this.fetch = fetch;
this.headerName = headerName;
this.customHeaders = customHeaders;

}

public start() {
Expand All @@ -87,10 +92,15 @@ export default class Metrics {
this.metricsInterval > 0
) {
// send first metrics after two seconds.
setTimeout(() => {
if(this.metricsIntervalInitial > 0) {
setTimeout(() => {
this.startTimer();
this.sendMetrics();
}, this.metricsIntervalInitial);
} else {
this.startTimer();
this.sendMetrics();
}, 2000);
}

}
}

Expand Down

0 comments on commit dcdf84d

Please sign in to comment.