Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/sdkFactory/__tests__/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ jest.mock('../../logger/sdkLogger', () => {
createLoggerAPI: () => loggerApiMock
};
});
const telemetryTrackerMock = 'telemetryTracker';
jest.mock('../../trackers/telemetryTracker', () => {
return {
telemetryTrackerFactory: () => telemetryTrackerMock
};
});

// IAsyncSDK, minimal params
const paramsForAsyncSDK = {
Expand Down Expand Up @@ -74,7 +80,7 @@ function assertModulesCalled(params: any) {
expect(SignalListenerInstanceMock.start).toBeCalledTimes(1);
}
if (params.splitApiFactory) {
expect(params.splitApiFactory.mock.calls).toEqual([[params.settings, params.platform]]);
expect(params.splitApiFactory.mock.calls).toEqual([[params.settings, params.platform, telemetryTrackerMock]]);
}
if (params.integrationsManagerFactory) {
expect(params.integrationsManagerFactory.mock.calls).toEqual([[{ settings: params.settings, storage: mockStorage }]]);
Expand Down
2 changes: 1 addition & 1 deletion src/sdkFactory/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
const telemetryTracker = telemetryTrackerFactory(storage.telemetry, platform.now);

// splitApi is used by SyncManager and Browser signal listener
const splitApi = splitApiFactory && splitApiFactory(settings, platform);
const splitApi = splitApiFactory && splitApiFactory(settings, platform, telemetryTracker);

const syncManager = syncManagerFactory && syncManagerFactory({
settings,
Expand Down
2 changes: 1 addition & 1 deletion src/sdkFactory/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export interface ISdkFactoryParams {

// Factory of Split Api (HTTP Client Service).
// It is not required when providing an asynchronous storage or offline SyncManager
splitApiFactory?: (settings: ISettings, platform: IPlatform) => ISplitApi,
splitApiFactory?: (settings: ISettings, platform: IPlatform, telemetryTracker: ITelemetryTracker) => ISplitApi,

// SyncManager factory.
// Not required when providing an asynchronous storage (consumer mode), but required in standalone mode to avoid SDK timeout.
Expand Down
6 changes: 3 additions & 3 deletions src/storages/inLocalStorage/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import { SplitsCacheInMemory } from '../inMemory/SplitsCacheInMemory';
import { DEFAULT_CACHE_EXPIRATION_IN_MILLIS } from '../../utils/constants/browser';
import { InMemoryStorageCSFactory } from '../inMemory/InMemoryStorageCS';
import { LOG_PREFIX } from './constants';
import { STORAGE_LOCALSTORAGE } from '../../utils/constants';
import { TelemetryCacheInMemory } from '../inMemory/TelemetryCacheInMemory';
import { LOCALHOST_MODE, STORAGE_LOCALSTORAGE } from '../../utils/constants';
import { shouldRecordTelemetry, TelemetryCacheInMemory } from '../inMemory/TelemetryCacheInMemory';

export interface InLocalStorageOptions {
prefix?: string
Expand Down Expand Up @@ -44,7 +44,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
impressions: new ImpressionsCacheInMemory(params.impressionsQueueSize),
impressionCounts: params.optimize ? new ImpressionCountsCacheInMemory() : undefined,
events: new EventsCacheInMemory(params.eventsQueueSize),
telemetry: new TelemetryCacheInMemory(),
telemetry: params.mode !== LOCALHOST_MODE && shouldRecordTelemetry() ? new TelemetryCacheInMemory() : undefined,

destroy() {
this.splits = new SplitsCacheInMemory();
Expand Down
4 changes: 2 additions & 2 deletions src/storages/inMemory/InMemoryStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ImpressionsCacheInMemory } from './ImpressionsCacheInMemory';
import { EventsCacheInMemory } from './EventsCacheInMemory';
import { IStorageFactoryParams, IStorageSync } from '../types';
import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
import { STORAGE_MEMORY } from '../../utils/constants';
import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
import { TelemetryCacheInMemory } from './TelemetryCacheInMemory';

/**
Expand All @@ -20,7 +20,7 @@ export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageS
impressions: new ImpressionsCacheInMemory(params.impressionsQueueSize),
impressionCounts: params.optimize ? new ImpressionCountsCacheInMemory() : undefined,
events: new EventsCacheInMemory(params.eventsQueueSize),
telemetry: new TelemetryCacheInMemory(),
telemetry: params.mode !== LOCALHOST_MODE ? new TelemetryCacheInMemory() : undefined, // Always track telemetry in standalone mode on server-side

// When using MEMORY we should clean all the caches to leave them empty
destroy() {
Expand Down
6 changes: 3 additions & 3 deletions src/storages/inMemory/InMemoryStorageCS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { ImpressionsCacheInMemory } from './ImpressionsCacheInMemory';
import { EventsCacheInMemory } from './EventsCacheInMemory';
import { IStorageSync, IStorageFactoryParams } from '../types';
import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
import { STORAGE_MEMORY } from '../../utils/constants';
import { TelemetryCacheInMemory } from './TelemetryCacheInMemory';
import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
import { shouldRecordTelemetry, TelemetryCacheInMemory } from './TelemetryCacheInMemory';

/**
* InMemory storage factory for standalone client-side SplitFactory
Expand All @@ -20,7 +20,7 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
impressions: new ImpressionsCacheInMemory(params.impressionsQueueSize),
impressionCounts: params.optimize ? new ImpressionCountsCacheInMemory() : undefined,
events: new EventsCacheInMemory(params.eventsQueueSize),
telemetry: new TelemetryCacheInMemory(),
telemetry: params.mode !== LOCALHOST_MODE && shouldRecordTelemetry() ? new TelemetryCacheInMemory() : undefined,

// When using MEMORY we should clean all the caches to leave them empty
destroy() {
Expand Down
9 changes: 9 additions & 0 deletions src/storages/inMemory/TelemetryCacheInMemory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ function newBuckets() {
return [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
}

const ACCEPTANCE_RANGE = 0.001;

/**
* Used on client-side. 0.1% of instances will track telemetry
*/
export function shouldRecordTelemetry() {
return Math.random() <= ACCEPTANCE_RANGE;
}

export class TelemetryCacheInMemory implements ITelemetryCacheSync {

private timeUntilReady?: number;
Expand Down
2 changes: 1 addition & 1 deletion src/sync/polling/pollingManagerCS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function pollingManagerCSFactory(
const { splitApi, storage, readiness, settings } = params;
const log = settings.log;

const splitsSyncTask: ISplitsSyncTask = splitsSyncTaskFactory(splitApi.fetchSplitChanges, storage, readiness, settings);
const splitsSyncTask: ISplitsSyncTask = splitsSyncTaskFactory(splitApi.fetchSplitChanges, storage, readiness, settings, true);

// Map of matching keys to their corresponding MySegmentsSyncTask.
const mySegmentsSyncTasks: Record<string, ISegmentsSyncTask> = {};
Expand Down
2 changes: 2 additions & 0 deletions src/sync/polling/syncTasks/splitsSyncTask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export function splitsSyncTaskFactory(
storage: IStorageSync,
readiness: IReadinessManager,
settings: ISettings,
isClientSide?: boolean
): ISplitsSyncTask {
return syncTaskFactory(
settings.log,
Expand All @@ -26,6 +27,7 @@ export function splitsSyncTaskFactory(
readiness.splits,
settings.startup.requestTimeoutBeforeReady,
settings.startup.retriesOnFailureBeforeReady,
isClientSide
),
settings.scheduler.featuresRefreshRate,
'splitChangesUpdater',
Expand Down
3 changes: 2 additions & 1 deletion src/sync/polling/updaters/splitChangesUpdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export function splitChangesUpdaterFactory(
splitsEventEmitter?: ISplitsEventEmitter,
requestTimeoutBeforeReady: number = 0,
retriesOnFailureBeforeReady: number = 0,
isClientSide?: boolean
): ISplitChangesUpdater {

let startingUp = true;
Expand Down Expand Up @@ -140,7 +141,7 @@ export function splitChangesUpdaterFactory(

if (splitsEventEmitter) {
// To emit SDK_SPLITS_ARRIVED for server-side SDK, we must check that all registered segments have been fetched
return Promise.resolve(!splitsEventEmitter.splitsArrived || (since !== splitChanges.till && checkAllSegmentsExist(segments)))
return Promise.resolve(!splitsEventEmitter.splitsArrived || (since !== splitChanges.till && (isClientSide || checkAllSegmentsExist(segments))))
.catch(() => false /** noop. just to handle a possible `checkAllSegmentsExist` rejection, before emitting SDK event */)
.then(emitSplitsArrivedEvent => {
// emit SDK events
Expand Down
6 changes: 3 additions & 3 deletions src/trackers/telemetryTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function telemetryTrackerFactory(
telemetryCache.recordException(method);
return; // Don't track latency on exceptions
case SDK_NOT_READY: // @ts-ignore ITelemetryCacheAsync doesn't implement the method
telemetryCache?.recordNonReadyUsage();
if (telemetryCache.recordNonReadyUsage) telemetryCache.recordNonReadyUsage();
}
telemetryCache.recordLatency(method, evalTime());
};
Expand All @@ -36,8 +36,8 @@ export function telemetryTrackerFactory(
else (telemetryCache as ITelemetryCacheSync).recordSuccessfulSync(operation, now());
};
},
sessionLength() {
(telemetryCache as ITelemetryCacheSync).recordSessionLength(startTime());
sessionLength() { // @ts-ignore ITelemetryCacheAsync doesn't implement the method
if (telemetryCache.recordSessionLength) telemetryCache.recordSessionLength(startTime());
},
streamingEvent(e, d) {
if (e === AUTH_REJECTION) {
Expand Down