@splitsoftware/splitio-commons 2.0.3-rc.0 → 2.1.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.txt +0 -4
- package/README.md +2 -2
- package/cjs/evaluator/index.js +0 -2
- package/cjs/listeners/browser.js +6 -4
- package/cjs/listeners/node.js +2 -2
- package/cjs/sdkClient/client.js +13 -13
- package/cjs/sdkClient/sdkClient.js +1 -1
- package/cjs/sdkFactory/index.js +14 -9
- package/cjs/sdkManager/index.js +1 -2
- package/cjs/storages/AbstractSplitsCacheAsync.js +0 -7
- package/cjs/storages/AbstractSplitsCacheSync.js +0 -7
- package/cjs/storages/KeyBuilderCS.js +3 -0
- package/cjs/storages/dataLoader.js +3 -2
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +1 -57
- package/cjs/storages/inLocalStorage/index.js +8 -7
- package/cjs/storages/inLocalStorage/validateCache.js +80 -0
- package/cjs/storages/inMemory/InMemoryStorage.js +3 -3
- package/cjs/storages/inMemory/InMemoryStorageCS.js +3 -4
- package/cjs/storages/inRedis/index.js +13 -9
- package/cjs/storages/pluggable/index.js +21 -16
- package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +3 -2
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +1 -10
- package/cjs/sync/streaming/pushManager.js +8 -6
- package/cjs/sync/submitters/impressionCountsSubmitter.js +4 -2
- package/cjs/sync/submitters/submitterManager.js +6 -3
- package/cjs/sync/syncManagerOnline.js +10 -4
- package/cjs/trackers/eventTracker.js +1 -1
- package/cjs/trackers/impressionsTracker.js +19 -18
- package/cjs/trackers/strategy/strategyDebug.js +11 -4
- package/cjs/trackers/strategy/strategyNone.js +16 -11
- package/cjs/trackers/strategy/strategyOptimized.js +21 -11
- package/cjs/utils/settingsValidation/index.js +1 -1
- package/cjs/utils/settingsValidation/storage/storageCS.js +1 -1
- package/esm/evaluator/index.js +0 -2
- package/esm/listeners/browser.js +3 -1
- package/esm/listeners/node.js +2 -2
- package/esm/sdkClient/client.js +13 -13
- package/esm/sdkClient/sdkClient.js +1 -1
- package/esm/sdkFactory/index.js +15 -10
- package/esm/sdkManager/index.js +1 -2
- package/esm/storages/AbstractSplitsCacheAsync.js +0 -7
- package/esm/storages/AbstractSplitsCacheSync.js +0 -7
- package/esm/storages/KeyBuilderCS.js +3 -0
- package/esm/storages/dataLoader.js +2 -1
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +1 -57
- package/esm/storages/inLocalStorage/index.js +9 -8
- package/esm/storages/inLocalStorage/validateCache.js +76 -0
- package/esm/storages/inMemory/InMemoryStorage.js +4 -4
- package/esm/storages/inMemory/InMemoryStorageCS.js +4 -5
- package/esm/storages/inRedis/index.js +14 -10
- package/esm/storages/pluggable/index.js +22 -17
- package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +3 -2
- package/esm/sync/polling/updaters/splitChangesUpdater.js +2 -11
- package/esm/sync/streaming/pushManager.js +8 -6
- package/esm/sync/submitters/impressionCountsSubmitter.js +4 -2
- package/esm/sync/submitters/submitterManager.js +6 -3
- package/esm/sync/syncManagerOnline.js +10 -4
- package/esm/trackers/eventTracker.js +1 -1
- package/esm/trackers/impressionsTracker.js +19 -18
- package/esm/trackers/strategy/strategyDebug.js +11 -4
- package/esm/trackers/strategy/strategyNone.js +16 -11
- package/esm/trackers/strategy/strategyOptimized.js +21 -11
- package/esm/utils/settingsValidation/index.js +1 -1
- package/esm/utils/settingsValidation/storage/storageCS.js +1 -1
- package/package.json +1 -1
- package/src/dtos/types.ts +1 -2
- package/src/evaluator/index.ts +0 -2
- package/src/evaluator/types.ts +1 -1
- package/src/listeners/browser.ts +3 -1
- package/src/listeners/node.ts +2 -2
- package/src/sdkClient/client.ts +11 -11
- package/src/sdkClient/sdkClient.ts +1 -1
- package/src/sdkFactory/index.ts +16 -11
- package/src/sdkFactory/types.ts +1 -1
- package/src/sdkManager/index.ts +1 -2
- package/src/storages/AbstractSplitsCacheAsync.ts +0 -8
- package/src/storages/AbstractSplitsCacheSync.ts +0 -8
- package/src/storages/KeyBuilderCS.ts +4 -0
- package/src/storages/dataLoader.ts +3 -1
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +1 -66
- package/src/storages/inLocalStorage/index.ts +12 -13
- package/src/storages/inLocalStorage/validateCache.ts +92 -0
- package/src/storages/inMemory/InMemoryStorage.ts +4 -4
- package/src/storages/inMemory/InMemoryStorageCS.ts +4 -5
- package/src/storages/inRedis/index.ts +10 -10
- package/src/storages/pluggable/index.ts +22 -17
- package/src/storages/types.ts +3 -6
- package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +6 -5
- package/src/sync/polling/updaters/splitChangesUpdater.ts +2 -11
- package/src/sync/streaming/pushManager.ts +8 -6
- package/src/sync/submitters/impressionCountsSubmitter.ts +4 -2
- package/src/sync/submitters/submitterManager.ts +4 -3
- package/src/sync/submitters/uniqueKeysSubmitter.ts +3 -2
- package/src/sync/syncManagerOnline.ts +11 -5
- package/src/trackers/eventTracker.ts +1 -1
- package/src/trackers/impressionsTracker.ts +19 -18
- package/src/trackers/strategy/strategyDebug.ts +11 -4
- package/src/trackers/strategy/strategyNone.ts +17 -11
- package/src/trackers/strategy/strategyOptimized.ts +20 -10
- package/src/trackers/types.ts +8 -2
- package/src/utils/lang/index.ts +1 -1
- package/src/utils/settingsValidation/index.ts +1 -1
- package/src/utils/settingsValidation/storage/storageCS.ts +1 -1
- package/types/index.d.ts +1 -1
- package/types/splitio.d.ts +26 -6
- package/cjs/utils/constants/browser.js +0 -5
- package/esm/utils/constants/browser.js +0 -2
- package/src/utils/constants/browser.ts +0 -2
|
@@ -7,22 +7,19 @@ import { KeyBuilderCS, myLargeSegmentsKeyBuilder } from '../KeyBuilderCS';
|
|
|
7
7
|
import { isLocalStorageAvailable } from '../../utils/env/isLocalStorageAvailable';
|
|
8
8
|
import { SplitsCacheInLocal } from './SplitsCacheInLocal';
|
|
9
9
|
import { MySegmentsCacheInLocal } from './MySegmentsCacheInLocal';
|
|
10
|
-
import { DEFAULT_CACHE_EXPIRATION_IN_MILLIS } from '../../utils/constants/browser';
|
|
11
10
|
import { InMemoryStorageCSFactory } from '../inMemory/InMemoryStorageCS';
|
|
12
11
|
import { LOG_PREFIX } from './constants';
|
|
13
|
-
import { STORAGE_LOCALSTORAGE } from '../../utils/constants';
|
|
12
|
+
import { DEBUG, NONE, STORAGE_LOCALSTORAGE } from '../../utils/constants';
|
|
14
13
|
import { shouldRecordTelemetry, TelemetryCacheInMemory } from '../inMemory/TelemetryCacheInMemory';
|
|
15
14
|
import { UniqueKeysCacheInMemoryCS } from '../inMemory/UniqueKeysCacheInMemoryCS';
|
|
16
15
|
import { getMatching } from '../../utils/key';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
prefix?: string
|
|
20
|
-
}
|
|
16
|
+
import { validateCache } from './validateCache';
|
|
17
|
+
import SplitIO from '../../../types/splitio';
|
|
21
18
|
|
|
22
19
|
/**
|
|
23
20
|
* InLocal storage factory for standalone client-side SplitFactory
|
|
24
21
|
*/
|
|
25
|
-
export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyncFactory {
|
|
22
|
+
export function InLocalStorage(options: SplitIO.InLocalStorageOptions = {}): IStorageSyncFactory {
|
|
26
23
|
|
|
27
24
|
const prefix = validatePrefix(options.prefix);
|
|
28
25
|
|
|
@@ -34,12 +31,11 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
|
|
|
34
31
|
return InMemoryStorageCSFactory(params);
|
|
35
32
|
}
|
|
36
33
|
|
|
37
|
-
const { settings, settings: { log, scheduler: { impressionsQueueSize, eventsQueueSize } } } = params;
|
|
34
|
+
const { settings, settings: { log, scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { impressionsMode } } } = params;
|
|
38
35
|
const matchingKey = getMatching(settings.core.key);
|
|
39
36
|
const keys = new KeyBuilderCS(prefix, matchingKey);
|
|
40
|
-
const expirationTimestamp = Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS;
|
|
41
37
|
|
|
42
|
-
const splits = new SplitsCacheInLocal(settings, keys
|
|
38
|
+
const splits = new SplitsCacheInLocal(settings, keys);
|
|
43
39
|
const segments = new MySegmentsCacheInLocal(log, keys);
|
|
44
40
|
const largeSegments = new MySegmentsCacheInLocal(log, myLargeSegmentsKeyBuilder(prefix, matchingKey));
|
|
45
41
|
|
|
@@ -48,10 +44,14 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
|
|
|
48
44
|
segments,
|
|
49
45
|
largeSegments,
|
|
50
46
|
impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
|
|
51
|
-
impressionCounts: new ImpressionCountsCacheInMemory(),
|
|
47
|
+
impressionCounts: impressionsMode !== DEBUG ? new ImpressionCountsCacheInMemory() : undefined,
|
|
52
48
|
events: new EventsCacheInMemory(eventsQueueSize),
|
|
53
49
|
telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
|
|
54
|
-
uniqueKeys: new UniqueKeysCacheInMemoryCS(),
|
|
50
|
+
uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemoryCS() : undefined,
|
|
51
|
+
|
|
52
|
+
validateCache() {
|
|
53
|
+
return validateCache(options, settings, keys, splits, segments, largeSegments);
|
|
54
|
+
},
|
|
55
55
|
|
|
56
56
|
destroy() { },
|
|
57
57
|
|
|
@@ -66,7 +66,6 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
|
|
|
66
66
|
impressionCounts: this.impressionCounts,
|
|
67
67
|
events: this.events,
|
|
68
68
|
telemetry: this.telemetry,
|
|
69
|
-
uniqueKeys: this.uniqueKeys,
|
|
70
69
|
|
|
71
70
|
destroy() { }
|
|
72
71
|
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { ISettings } from '../../types';
|
|
2
|
+
import { isFiniteNumber, isNaNNumber } from '../../utils/lang';
|
|
3
|
+
import { getStorageHash } from '../KeyBuilder';
|
|
4
|
+
import { LOG_PREFIX } from './constants';
|
|
5
|
+
import type { SplitsCacheInLocal } from './SplitsCacheInLocal';
|
|
6
|
+
import type { MySegmentsCacheInLocal } from './MySegmentsCacheInLocal';
|
|
7
|
+
import { KeyBuilderCS } from '../KeyBuilderCS';
|
|
8
|
+
import SplitIO from '../../../types/splitio';
|
|
9
|
+
|
|
10
|
+
// milliseconds in a day
|
|
11
|
+
const DEFAULT_CACHE_EXPIRATION_IN_DAYS = 10;
|
|
12
|
+
const MILLIS_IN_A_DAY = 86400000;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Validates if cache should be cleared and sets the cache `hash` if needed.
|
|
16
|
+
*
|
|
17
|
+
* @returns `true` if cache should be cleared, `false` otherwise
|
|
18
|
+
*/
|
|
19
|
+
function validateExpiration(options: SplitIO.InLocalStorageOptions, settings: ISettings, keys: KeyBuilderCS, currentTimestamp: number, isThereCache: boolean) {
|
|
20
|
+
const { log } = settings;
|
|
21
|
+
|
|
22
|
+
// Check expiration
|
|
23
|
+
const lastUpdatedTimestamp = parseInt(localStorage.getItem(keys.buildLastUpdatedKey()) as string, 10);
|
|
24
|
+
if (!isNaNNumber(lastUpdatedTimestamp)) {
|
|
25
|
+
const cacheExpirationInDays = isFiniteNumber(options.expirationDays) && options.expirationDays >= 1 ? options.expirationDays : DEFAULT_CACHE_EXPIRATION_IN_DAYS;
|
|
26
|
+
const expirationTimestamp = currentTimestamp - MILLIS_IN_A_DAY * cacheExpirationInDays;
|
|
27
|
+
if (lastUpdatedTimestamp < expirationTimestamp) {
|
|
28
|
+
log.info(LOG_PREFIX + 'Cache expired more than ' + cacheExpirationInDays + ' days ago. Cleaning up cache');
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Check hash
|
|
34
|
+
const storageHashKey = keys.buildHashKey();
|
|
35
|
+
const storageHash = localStorage.getItem(storageHashKey);
|
|
36
|
+
const currentStorageHash = getStorageHash(settings);
|
|
37
|
+
|
|
38
|
+
if (storageHash !== currentStorageHash) {
|
|
39
|
+
try {
|
|
40
|
+
localStorage.setItem(storageHashKey, currentStorageHash);
|
|
41
|
+
} catch (e) {
|
|
42
|
+
log.error(LOG_PREFIX + e);
|
|
43
|
+
}
|
|
44
|
+
if (isThereCache) {
|
|
45
|
+
log.info(LOG_PREFIX + 'SDK key, flags filter criteria or flags spec version has changed. Cleaning up cache');
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
return false; // No cache to clear
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Clear on init
|
|
52
|
+
if (options.clearOnInit) {
|
|
53
|
+
const lastClearTimestamp = parseInt(localStorage.getItem(keys.buildLastClear()) as string, 10);
|
|
54
|
+
|
|
55
|
+
if (isNaNNumber(lastClearTimestamp) || lastClearTimestamp < currentTimestamp - MILLIS_IN_A_DAY) {
|
|
56
|
+
log.info(LOG_PREFIX + 'clearOnInit was set and cache was not cleared in the last 24 hours. Cleaning up cache');
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Clean cache if:
|
|
64
|
+
* - it has expired, i.e., its `lastUpdated` timestamp is older than the given `expirationTimestamp`
|
|
65
|
+
* - its hash has changed, i.e., the SDK key, flags filter criteria or flags spec version was modified
|
|
66
|
+
* - `clearOnInit` was set and cache was not cleared in the last 24 hours
|
|
67
|
+
*
|
|
68
|
+
* @returns `true` if cache is ready to be used, `false` otherwise (cache was cleared or there is no cache)
|
|
69
|
+
*/
|
|
70
|
+
export function validateCache(options: SplitIO.InLocalStorageOptions, settings: ISettings, keys: KeyBuilderCS, splits: SplitsCacheInLocal, segments: MySegmentsCacheInLocal, largeSegments: MySegmentsCacheInLocal): boolean {
|
|
71
|
+
|
|
72
|
+
const currentTimestamp = Date.now();
|
|
73
|
+
const isThereCache = splits.getChangeNumber() > -1;
|
|
74
|
+
|
|
75
|
+
if (validateExpiration(options, settings, keys, currentTimestamp, isThereCache)) {
|
|
76
|
+
splits.clear();
|
|
77
|
+
segments.clear();
|
|
78
|
+
largeSegments.clear();
|
|
79
|
+
|
|
80
|
+
// Update last clear timestamp
|
|
81
|
+
try {
|
|
82
|
+
localStorage.setItem(keys.buildLastClear(), currentTimestamp + '');
|
|
83
|
+
} catch (e) {
|
|
84
|
+
settings.log.error(LOG_PREFIX + e);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Check if ready from cache
|
|
91
|
+
return isThereCache;
|
|
92
|
+
}
|
|
@@ -4,7 +4,7 @@ import { ImpressionsCacheInMemory } from './ImpressionsCacheInMemory';
|
|
|
4
4
|
import { EventsCacheInMemory } from './EventsCacheInMemory';
|
|
5
5
|
import { IStorageFactoryParams, IStorageSync } from '../types';
|
|
6
6
|
import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
|
|
7
|
-
import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
|
|
7
|
+
import { DEBUG, LOCALHOST_MODE, NONE, STORAGE_MEMORY } from '../../utils/constants';
|
|
8
8
|
import { shouldRecordTelemetry, TelemetryCacheInMemory } from './TelemetryCacheInMemory';
|
|
9
9
|
import { UniqueKeysCacheInMemory } from './UniqueKeysCacheInMemory';
|
|
10
10
|
|
|
@@ -14,7 +14,7 @@ import { UniqueKeysCacheInMemory } from './UniqueKeysCacheInMemory';
|
|
|
14
14
|
* @param params - parameters required by EventsCacheSync
|
|
15
15
|
*/
|
|
16
16
|
export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageSync {
|
|
17
|
-
const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { __splitFiltersValidation } } } = params;
|
|
17
|
+
const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { impressionsMode, __splitFiltersValidation } } } = params;
|
|
18
18
|
|
|
19
19
|
const splits = new SplitsCacheInMemory(__splitFiltersValidation);
|
|
20
20
|
const segments = new SegmentsCacheInMemory();
|
|
@@ -23,10 +23,10 @@ export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageS
|
|
|
23
23
|
splits,
|
|
24
24
|
segments,
|
|
25
25
|
impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
|
|
26
|
-
impressionCounts: new ImpressionCountsCacheInMemory(),
|
|
26
|
+
impressionCounts: impressionsMode !== DEBUG ? new ImpressionCountsCacheInMemory() : undefined,
|
|
27
27
|
events: new EventsCacheInMemory(eventsQueueSize),
|
|
28
28
|
telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
|
|
29
|
-
uniqueKeys: new UniqueKeysCacheInMemory(),
|
|
29
|
+
uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemory() : undefined,
|
|
30
30
|
|
|
31
31
|
destroy() { }
|
|
32
32
|
};
|
|
@@ -4,7 +4,7 @@ import { ImpressionsCacheInMemory } from './ImpressionsCacheInMemory';
|
|
|
4
4
|
import { EventsCacheInMemory } from './EventsCacheInMemory';
|
|
5
5
|
import { IStorageSync, IStorageFactoryParams } from '../types';
|
|
6
6
|
import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
|
|
7
|
-
import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
|
|
7
|
+
import { DEBUG, LOCALHOST_MODE, NONE, STORAGE_MEMORY } from '../../utils/constants';
|
|
8
8
|
import { shouldRecordTelemetry, TelemetryCacheInMemory } from './TelemetryCacheInMemory';
|
|
9
9
|
import { UniqueKeysCacheInMemoryCS } from './UniqueKeysCacheInMemoryCS';
|
|
10
10
|
|
|
@@ -14,7 +14,7 @@ import { UniqueKeysCacheInMemoryCS } from './UniqueKeysCacheInMemoryCS';
|
|
|
14
14
|
* @param params - parameters required by EventsCacheSync
|
|
15
15
|
*/
|
|
16
16
|
export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorageSync {
|
|
17
|
-
const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize }, sync: { __splitFiltersValidation } } } = params;
|
|
17
|
+
const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { impressionsMode, __splitFiltersValidation } } } = params;
|
|
18
18
|
|
|
19
19
|
const splits = new SplitsCacheInMemory(__splitFiltersValidation);
|
|
20
20
|
const segments = new MySegmentsCacheInMemory();
|
|
@@ -25,10 +25,10 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
|
|
|
25
25
|
segments,
|
|
26
26
|
largeSegments,
|
|
27
27
|
impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
|
|
28
|
-
impressionCounts: new ImpressionCountsCacheInMemory(),
|
|
28
|
+
impressionCounts: impressionsMode !== DEBUG ? new ImpressionCountsCacheInMemory() : undefined,
|
|
29
29
|
events: new EventsCacheInMemory(eventsQueueSize),
|
|
30
30
|
telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
|
|
31
|
-
uniqueKeys: new UniqueKeysCacheInMemoryCS(),
|
|
31
|
+
uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemoryCS() : undefined,
|
|
32
32
|
|
|
33
33
|
destroy() { },
|
|
34
34
|
|
|
@@ -42,7 +42,6 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
|
|
|
42
42
|
impressionCounts: this.impressionCounts,
|
|
43
43
|
events: this.events,
|
|
44
44
|
telemetry: this.telemetry,
|
|
45
|
-
uniqueKeys: this.uniqueKeys,
|
|
46
45
|
|
|
47
46
|
destroy() { }
|
|
48
47
|
};
|
|
@@ -6,7 +6,7 @@ import { SplitsCacheInRedis } from './SplitsCacheInRedis';
|
|
|
6
6
|
import { SegmentsCacheInRedis } from './SegmentsCacheInRedis';
|
|
7
7
|
import { ImpressionsCacheInRedis } from './ImpressionsCacheInRedis';
|
|
8
8
|
import { EventsCacheInRedis } from './EventsCacheInRedis';
|
|
9
|
-
import { STORAGE_REDIS } from '../../utils/constants';
|
|
9
|
+
import { DEBUG, NONE, STORAGE_REDIS } from '../../utils/constants';
|
|
10
10
|
import { TelemetryCacheInRedis } from './TelemetryCacheInRedis';
|
|
11
11
|
import { UniqueKeysCacheInRedis } from './UniqueKeysCacheInRedis';
|
|
12
12
|
import { ImpressionCountsCacheInRedis } from './ImpressionCountsCacheInRedis';
|
|
@@ -30,19 +30,19 @@ export function InRedisStorage(options: InRedisStorageOptions = {}): IStorageAsy
|
|
|
30
30
|
const prefix = validatePrefix(options.prefix);
|
|
31
31
|
|
|
32
32
|
function InRedisStorageFactory(params: IStorageFactoryParams): IStorageAsync {
|
|
33
|
-
const { onReadyCb, settings, settings: { log } } = params;
|
|
33
|
+
const { onReadyCb, settings, settings: { log, sync: { impressionsMode } } } = params;
|
|
34
34
|
const metadata = metadataBuilder(settings);
|
|
35
35
|
const keys = new KeyBuilderSS(prefix, metadata);
|
|
36
36
|
const redisClient: RedisAdapter = new RD(log, options.options || {});
|
|
37
37
|
const telemetry = new TelemetryCacheInRedis(log, keys, redisClient);
|
|
38
|
-
const impressionCountsCache = new ImpressionCountsCacheInRedis(log, keys.buildImpressionsCountKey(), redisClient);
|
|
39
|
-
const uniqueKeysCache = new UniqueKeysCacheInRedis(log, keys.buildUniqueKeysKey(), redisClient);
|
|
38
|
+
const impressionCountsCache = impressionsMode !== DEBUG ? new ImpressionCountsCacheInRedis(log, keys.buildImpressionsCountKey(), redisClient) : undefined;
|
|
39
|
+
const uniqueKeysCache = impressionsMode === NONE ? new UniqueKeysCacheInRedis(log, keys.buildUniqueKeysKey(), redisClient) : undefined;
|
|
40
40
|
|
|
41
41
|
// subscription to Redis connect event in order to emit SDK_READY event on consumer mode
|
|
42
42
|
redisClient.on('connect', () => {
|
|
43
43
|
onReadyCb();
|
|
44
|
-
impressionCountsCache.start();
|
|
45
|
-
uniqueKeysCache.start();
|
|
44
|
+
if (impressionCountsCache) impressionCountsCache.start();
|
|
45
|
+
if (uniqueKeysCache) uniqueKeysCache.start();
|
|
46
46
|
|
|
47
47
|
// Synchronize config
|
|
48
48
|
telemetry.recordConfig();
|
|
@@ -60,10 +60,10 @@ export function InRedisStorage(options: InRedisStorageOptions = {}): IStorageAsy
|
|
|
60
60
|
// When using REDIS we should:
|
|
61
61
|
// 1- Disconnect from the storage
|
|
62
62
|
destroy(): Promise<void> {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
let promises = [];
|
|
64
|
+
if (impressionCountsCache) promises.push(impressionCountsCache.stop());
|
|
65
|
+
if (uniqueKeysCache) promises.push(uniqueKeysCache.stop());
|
|
66
|
+
return Promise.all(promises).then(() => { redisClient.disconnect(); });
|
|
67
67
|
// @TODO check that caches works as expected when redisClient is disconnected
|
|
68
68
|
}
|
|
69
69
|
};
|
|
@@ -8,7 +8,7 @@ import { EventsCachePluggable } from './EventsCachePluggable';
|
|
|
8
8
|
import { wrapperAdapter, METHODS_TO_PROMISE_WRAP } from './wrapperAdapter';
|
|
9
9
|
import { isObject } from '../../utils/lang';
|
|
10
10
|
import { getStorageHash, validatePrefix } from '../KeyBuilder';
|
|
11
|
-
import { CONSUMER_PARTIAL_MODE, STORAGE_PLUGGABLE } from '../../utils/constants';
|
|
11
|
+
import { CONSUMER_PARTIAL_MODE, DEBUG, NONE, STORAGE_PLUGGABLE } from '../../utils/constants';
|
|
12
12
|
import { ImpressionsCacheInMemory } from '../inMemory/ImpressionsCacheInMemory';
|
|
13
13
|
import { EventsCacheInMemory } from '../inMemory/EventsCacheInMemory';
|
|
14
14
|
import { ImpressionCountsCacheInMemory } from '../inMemory/ImpressionCountsCacheInMemory';
|
|
@@ -63,32 +63,37 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
|
|
|
63
63
|
const prefix = validatePrefix(options.prefix);
|
|
64
64
|
|
|
65
65
|
function PluggableStorageFactory(params: IStorageFactoryParams): IStorageAsync {
|
|
66
|
-
const { onReadyCb, settings, settings: { log, mode, scheduler: { impressionsQueueSize, eventsQueueSize } } } = params;
|
|
66
|
+
const { onReadyCb, settings, settings: { log, mode, sync: { impressionsMode }, scheduler: { impressionsQueueSize, eventsQueueSize } } } = params;
|
|
67
67
|
const metadata = metadataBuilder(settings);
|
|
68
68
|
const keys = new KeyBuilderSS(prefix, metadata);
|
|
69
69
|
const wrapper = wrapperAdapter(log, options.wrapper);
|
|
70
70
|
|
|
71
|
-
const
|
|
71
|
+
const isSyncronizer = mode === undefined; // If mode is not defined, the synchronizer is running
|
|
72
72
|
const isPartialConsumer = mode === CONSUMER_PARTIAL_MODE;
|
|
73
73
|
|
|
74
|
-
const telemetry = shouldRecordTelemetry(params) ||
|
|
74
|
+
const telemetry = shouldRecordTelemetry(params) || isSyncronizer ?
|
|
75
75
|
isPartialConsumer ?
|
|
76
76
|
new TelemetryCacheInMemory() :
|
|
77
77
|
new TelemetryCachePluggable(log, keys, wrapper) :
|
|
78
78
|
undefined;
|
|
79
79
|
|
|
80
|
-
const impressionCountsCache =
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
const impressionCountsCache = impressionsMode !== DEBUG || isSyncronizer ?
|
|
81
|
+
isPartialConsumer ?
|
|
82
|
+
new ImpressionCountsCacheInMemory() :
|
|
83
|
+
new ImpressionCountsCachePluggable(log, keys.buildImpressionsCountKey(), wrapper) :
|
|
84
|
+
undefined;
|
|
83
85
|
|
|
84
|
-
const uniqueKeysCache =
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
const uniqueKeysCache = impressionsMode === NONE || isSyncronizer ?
|
|
87
|
+
isPartialConsumer ?
|
|
88
|
+
settings.core.key === undefined ? new UniqueKeysCacheInMemory() : new UniqueKeysCacheInMemoryCS() :
|
|
89
|
+
new UniqueKeysCachePluggable(log, keys.buildUniqueKeysKey(), wrapper) :
|
|
90
|
+
undefined;
|
|
87
91
|
|
|
88
92
|
// Connects to wrapper and emits SDK_READY event on main client
|
|
89
93
|
const connectPromise = wrapper.connect().then(() => {
|
|
90
|
-
if (
|
|
91
|
-
//
|
|
94
|
+
if (isSyncronizer) {
|
|
95
|
+
// @TODO reuse InLocalStorage::validateCache logic
|
|
96
|
+
// In standalone or producer mode, clear storage if SDK key, flags filter criteria or flags spec version was modified
|
|
92
97
|
return wrapper.get(keys.buildHashKey()).then((hash) => {
|
|
93
98
|
const currentHash = getStorageHash(settings);
|
|
94
99
|
if (hash !== currentHash) {
|
|
@@ -102,8 +107,8 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
|
|
|
102
107
|
});
|
|
103
108
|
} else {
|
|
104
109
|
// Start periodic flush of async storages if not running synchronizer (producer mode)
|
|
105
|
-
if ((impressionCountsCache as ImpressionCountsCachePluggable).start) (impressionCountsCache as ImpressionCountsCachePluggable).start();
|
|
106
|
-
if ((uniqueKeysCache as UniqueKeysCachePluggable).start) (uniqueKeysCache as UniqueKeysCachePluggable).start();
|
|
110
|
+
if (impressionCountsCache && (impressionCountsCache as ImpressionCountsCachePluggable).start) (impressionCountsCache as ImpressionCountsCachePluggable).start();
|
|
111
|
+
if (uniqueKeysCache && (uniqueKeysCache as UniqueKeysCachePluggable).start) (uniqueKeysCache as UniqueKeysCachePluggable).start();
|
|
107
112
|
if (telemetry && (telemetry as ITelemetryCacheAsync).recordConfig) (telemetry as ITelemetryCacheAsync).recordConfig();
|
|
108
113
|
|
|
109
114
|
onReadyCb();
|
|
@@ -125,9 +130,9 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
|
|
|
125
130
|
|
|
126
131
|
// Stop periodic flush and disconnect the underlying storage
|
|
127
132
|
destroy() {
|
|
128
|
-
return Promise.all(
|
|
129
|
-
(impressionCountsCache as ImpressionCountsCachePluggable).stop && (impressionCountsCache as ImpressionCountsCachePluggable).stop(),
|
|
130
|
-
(uniqueKeysCache as UniqueKeysCachePluggable).stop && (uniqueKeysCache as UniqueKeysCachePluggable).stop(),
|
|
133
|
+
return Promise.all(isSyncronizer ? [] : [
|
|
134
|
+
impressionCountsCache && (impressionCountsCache as ImpressionCountsCachePluggable).stop && (impressionCountsCache as ImpressionCountsCachePluggable).stop(),
|
|
135
|
+
uniqueKeysCache && (uniqueKeysCache as UniqueKeysCachePluggable).stop && (uniqueKeysCache as UniqueKeysCachePluggable).stop(),
|
|
131
136
|
]).then(() => wrapper.disconnect());
|
|
132
137
|
},
|
|
133
138
|
|
package/src/storages/types.ts
CHANGED
|
@@ -191,8 +191,6 @@ export interface ISplitsCacheBase {
|
|
|
191
191
|
// only for Client-Side. Returns true if the storage is not synchronized yet (getChangeNumber() === -1) or contains a FF using segments or large segments
|
|
192
192
|
usesSegments(): MaybeThenable<boolean>,
|
|
193
193
|
clear(): MaybeThenable<boolean | void>,
|
|
194
|
-
// should never reject or throw an exception. Instead return false by default, to avoid emitting SDK_READY_FROM_CACHE.
|
|
195
|
-
checkCache(): MaybeThenable<boolean>,
|
|
196
194
|
killLocally(name: string, defaultTreatment: string, changeNumber: number): MaybeThenable<boolean>,
|
|
197
195
|
getNamesByFlagSets(flagSets: string[]): MaybeThenable<Set<string>[]>
|
|
198
196
|
}
|
|
@@ -209,7 +207,6 @@ export interface ISplitsCacheSync extends ISplitsCacheBase {
|
|
|
209
207
|
trafficTypeExists(trafficType: string): boolean,
|
|
210
208
|
usesSegments(): boolean,
|
|
211
209
|
clear(): void,
|
|
212
|
-
checkCache(): boolean,
|
|
213
210
|
killLocally(name: string, defaultTreatment: string, changeNumber: number): boolean,
|
|
214
211
|
getNamesByFlagSets(flagSets: string[]): Set<string>[]
|
|
215
212
|
}
|
|
@@ -226,7 +223,6 @@ export interface ISplitsCacheAsync extends ISplitsCacheBase {
|
|
|
226
223
|
trafficTypeExists(trafficType: string): Promise<boolean>,
|
|
227
224
|
usesSegments(): Promise<boolean>,
|
|
228
225
|
clear(): Promise<boolean | void>,
|
|
229
|
-
checkCache(): Promise<boolean>,
|
|
230
226
|
killLocally(name: string, defaultTreatment: string, changeNumber: number): Promise<boolean>,
|
|
231
227
|
getNamesByFlagSets(flagSets: string[]): Promise<Set<string>[]>
|
|
232
228
|
}
|
|
@@ -439,10 +435,10 @@ export interface IStorageBase<
|
|
|
439
435
|
splits: TSplitsCache,
|
|
440
436
|
segments: TSegmentsCache,
|
|
441
437
|
impressions: TImpressionsCache,
|
|
442
|
-
impressionCounts
|
|
438
|
+
impressionCounts?: TImpressionsCountCache,
|
|
443
439
|
events: TEventsCache,
|
|
444
440
|
telemetry?: TTelemetryCache,
|
|
445
|
-
uniqueKeys
|
|
441
|
+
uniqueKeys?: TUniqueKeysCache,
|
|
446
442
|
destroy(): void | Promise<void>,
|
|
447
443
|
shared?: (matchingKey: string, onReadyCb: (error?: any) => void) => this
|
|
448
444
|
}
|
|
@@ -457,6 +453,7 @@ export interface IStorageSync extends IStorageBase<
|
|
|
457
453
|
IUniqueKeysCacheSync
|
|
458
454
|
> {
|
|
459
455
|
// Defined in client-side
|
|
456
|
+
validateCache?: () => boolean, // @TODO support async
|
|
460
457
|
largeSegments?: ISegmentsCacheSync,
|
|
461
458
|
}
|
|
462
459
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { forOwn } from '../../../utils/lang';
|
|
2
2
|
import { IReadinessManager } from '../../../readiness/types';
|
|
3
|
-
import {
|
|
3
|
+
import { IStorageSync } from '../../../storages/types';
|
|
4
4
|
import { ISplitsParser } from '../splitsParser/types';
|
|
5
5
|
import { ISplit, ISplitPartial } from '../../../dtos/types';
|
|
6
6
|
import { syncTaskFactory } from '../../syncTask';
|
|
@@ -15,7 +15,7 @@ import { SYNC_OFFLINE_DATA, ERROR_SYNC_OFFLINE_LOADING } from '../../../logger/c
|
|
|
15
15
|
*/
|
|
16
16
|
export function fromObjectUpdaterFactory(
|
|
17
17
|
splitsParser: ISplitsParser,
|
|
18
|
-
storage:
|
|
18
|
+
storage: Pick<IStorageSync, 'splits' | 'validateCache'>,
|
|
19
19
|
readiness: IReadinessManager,
|
|
20
20
|
settings: ISettings,
|
|
21
21
|
): () => Promise<boolean> {
|
|
@@ -60,9 +60,10 @@ export function fromObjectUpdaterFactory(
|
|
|
60
60
|
|
|
61
61
|
if (startingUp) {
|
|
62
62
|
startingUp = false;
|
|
63
|
-
|
|
63
|
+
const isCacheLoaded = storage.validateCache ? storage.validateCache() : false;
|
|
64
|
+
Promise.resolve().then(() => {
|
|
64
65
|
// Emits SDK_READY_FROM_CACHE
|
|
65
|
-
if (
|
|
66
|
+
if (isCacheLoaded) readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
|
|
66
67
|
// Emits SDK_READY
|
|
67
68
|
readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
|
|
68
69
|
});
|
|
@@ -80,7 +81,7 @@ export function fromObjectUpdaterFactory(
|
|
|
80
81
|
*/
|
|
81
82
|
export function fromObjectSyncTaskFactory(
|
|
82
83
|
splitsParser: ISplitsParser,
|
|
83
|
-
storage:
|
|
84
|
+
storage: Pick<IStorageSync, 'splits' | 'validateCache'>,
|
|
84
85
|
readiness: IReadinessManager,
|
|
85
86
|
settings: ISettings
|
|
86
87
|
): ISyncTask<[], boolean> {
|
|
@@ -3,7 +3,7 @@ import { ISplitChangesFetcher } from '../fetchers/types';
|
|
|
3
3
|
import { ISplit, ISplitChangesResponse, ISplitFiltersValidation } from '../../../dtos/types';
|
|
4
4
|
import { ISplitsEventEmitter } from '../../../readiness/types';
|
|
5
5
|
import { timeout } from '../../../utils/promise/timeout';
|
|
6
|
-
import { SDK_SPLITS_ARRIVED
|
|
6
|
+
import { SDK_SPLITS_ARRIVED } from '../../../readiness/constants';
|
|
7
7
|
import { ILogger } from '../../../logger/types';
|
|
8
8
|
import { SYNC_SPLITS_FETCH, SYNC_SPLITS_NEW, SYNC_SPLITS_REMOVED, SYNC_SPLITS_SEGMENTS, SYNC_SPLITS_FETCH_FAILS, SYNC_SPLITS_FETCH_RETRY } from '../../../logger/constants';
|
|
9
9
|
import { startsWith } from '../../../utils/lang';
|
|
@@ -153,7 +153,7 @@ export function splitChangesUpdaterFactory(
|
|
|
153
153
|
*/
|
|
154
154
|
function _splitChangesUpdater(since: number, retry = 0): Promise<boolean> {
|
|
155
155
|
log.debug(SYNC_SPLITS_FETCH, [since]);
|
|
156
|
-
|
|
156
|
+
return Promise.resolve(splitUpdateNotification ?
|
|
157
157
|
{ splits: [splitUpdateNotification.payload], till: splitUpdateNotification.changeNumber } :
|
|
158
158
|
splitChangesFetcher(since, noCache, till, _promiseDecorator)
|
|
159
159
|
)
|
|
@@ -200,15 +200,6 @@ export function splitChangesUpdaterFactory(
|
|
|
200
200
|
}
|
|
201
201
|
return false;
|
|
202
202
|
});
|
|
203
|
-
|
|
204
|
-
// After triggering the requests, if we have cached splits information let's notify that to emit SDK_READY_FROM_CACHE.
|
|
205
|
-
// Wrapping in a promise since checkCache can be async.
|
|
206
|
-
if (splitsEventEmitter && startingUp) {
|
|
207
|
-
Promise.resolve(splits.checkCache()).then(isCacheReady => {
|
|
208
|
-
if (isCacheReady) splitsEventEmitter.emit(SDK_SPLITS_CACHE_LOADED);
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
return fetcherPromise;
|
|
212
203
|
}
|
|
213
204
|
|
|
214
205
|
let sincePromise = Promise.resolve(splits.getChangeNumber()); // `getChangeNumber` never rejects or throws error
|
|
@@ -349,12 +349,14 @@ export function pushManagerFactory(
|
|
|
349
349
|
// Reconnects in case of a new client.
|
|
350
350
|
// Run in next event-loop cycle to save authentication calls
|
|
351
351
|
// in case multiple clients are created in the current cycle.
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
connectForNewClient
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
352
|
+
if (this.isRunning()) {
|
|
353
|
+
setTimeout(function checkForReconnect() {
|
|
354
|
+
if (connectForNewClient) {
|
|
355
|
+
connectForNewClient = false;
|
|
356
|
+
connectPush();
|
|
357
|
+
}
|
|
358
|
+
}, 0);
|
|
359
|
+
}
|
|
358
360
|
}
|
|
359
361
|
},
|
|
360
362
|
// [Only for client-side]
|
|
@@ -39,6 +39,8 @@ export function impressionCountsSubmitterFactory(params: ISdkFactoryContextSync)
|
|
|
39
39
|
storage: { impressionCounts }
|
|
40
40
|
} = params;
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
if (impressionCounts) {
|
|
43
|
+
// retry impressions counts only once.
|
|
44
|
+
return submitterFactory(log, postTestImpressionsCount, impressionCounts, IMPRESSIONS_COUNT_RATE, 'impression counts', fromImpressionCountsCollector, 1);
|
|
45
|
+
}
|
|
44
46
|
}
|
|
@@ -10,12 +10,13 @@ export function submitterManagerFactory(params: ISdkFactoryContextSync): ISubmit
|
|
|
10
10
|
|
|
11
11
|
const submitters = [
|
|
12
12
|
impressionsSubmitterFactory(params),
|
|
13
|
-
eventsSubmitterFactory(params)
|
|
14
|
-
impressionCountsSubmitterFactory(params),
|
|
15
|
-
uniqueKeysSubmitterFactory(params)
|
|
13
|
+
eventsSubmitterFactory(params)
|
|
16
14
|
];
|
|
17
15
|
|
|
16
|
+
const impressionCountsSubmitter = impressionCountsSubmitterFactory(params);
|
|
17
|
+
if (impressionCountsSubmitter) submitters.push(impressionCountsSubmitter);
|
|
18
18
|
const telemetrySubmitter = telemetrySubmitterFactory(params);
|
|
19
|
+
if (params.storage.uniqueKeys) submitters.push(uniqueKeysSubmitterFactory(params));
|
|
19
20
|
|
|
20
21
|
return {
|
|
21
22
|
// `onlyTelemetry` true if SDK is created with userConsent not GRANTED
|
|
@@ -19,10 +19,10 @@ export function uniqueKeysSubmitterFactory(params: ISdkFactoryContextSync) {
|
|
|
19
19
|
const isClientSide = key !== undefined;
|
|
20
20
|
const postUniqueKeysBulk = isClientSide ? postUniqueKeysBulkCs : postUniqueKeysBulkSs;
|
|
21
21
|
|
|
22
|
-
const syncTask = submitterFactory(log, postUniqueKeysBulk, uniqueKeys
|
|
22
|
+
const syncTask = submitterFactory(log, postUniqueKeysBulk, uniqueKeys!, UNIQUE_KEYS_RATE, DATA_NAME);
|
|
23
23
|
|
|
24
24
|
// register unique keys submitter to be executed when uniqueKeys cache is full
|
|
25
|
-
uniqueKeys
|
|
25
|
+
uniqueKeys!.setOnFullQueueCb(() => {
|
|
26
26
|
if (syncTask.isRunning()) {
|
|
27
27
|
log.info(SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]);
|
|
28
28
|
syncTask.execute();
|
|
@@ -33,3 +33,4 @@ export function uniqueKeysSubmitterFactory(params: ISdkFactoryContextSync) {
|
|
|
33
33
|
|
|
34
34
|
return syncTask;
|
|
35
35
|
}
|
|
36
|
+
|
|
@@ -9,6 +9,7 @@ import { SYNC_START_POLLING, SYNC_CONTINUE_POLLING, SYNC_STOP_POLLING } from '..
|
|
|
9
9
|
import { isConsentGranted } from '../consent';
|
|
10
10
|
import { POLLING, STREAMING, SYNC_MODE_UPDATE } from '../utils/constants';
|
|
11
11
|
import { ISdkFactoryContextSync } from '../sdkFactory/types';
|
|
12
|
+
import { SDK_SPLITS_CACHE_LOADED } from '../readiness/constants';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Online SyncManager factory.
|
|
@@ -28,7 +29,7 @@ export function syncManagerOnlineFactory(
|
|
|
28
29
|
*/
|
|
29
30
|
return function (params: ISdkFactoryContextSync): ISyncManagerCS {
|
|
30
31
|
|
|
31
|
-
const { settings, settings: { log, streamingEnabled, sync: { enabled: syncEnabled } },
|
|
32
|
+
const { settings, settings: { log, streamingEnabled, sync: { enabled: syncEnabled } }, telemetryTracker, storage, readiness } = params;
|
|
32
33
|
|
|
33
34
|
/** Polling Manager */
|
|
34
35
|
const pollingManager = pollingManagerFactory && pollingManagerFactory(params);
|
|
@@ -87,6 +88,11 @@ export function syncManagerOnlineFactory(
|
|
|
87
88
|
start() {
|
|
88
89
|
running = true;
|
|
89
90
|
|
|
91
|
+
if (startFirstTime) {
|
|
92
|
+
const isCacheLoaded = storage.validateCache ? storage.validateCache() : false;
|
|
93
|
+
if (isCacheLoaded) Promise.resolve().then(() => { readiness.splits.emit(SDK_SPLITS_CACHE_LOADED); });
|
|
94
|
+
}
|
|
95
|
+
|
|
90
96
|
// start syncing splits and segments
|
|
91
97
|
if (pollingManager) {
|
|
92
98
|
|
|
@@ -96,7 +102,6 @@ export function syncManagerOnlineFactory(
|
|
|
96
102
|
// Doesn't call `syncAll` when the syncManager is resuming
|
|
97
103
|
if (startFirstTime) {
|
|
98
104
|
pollingManager.syncAll();
|
|
99
|
-
startFirstTime = false;
|
|
100
105
|
}
|
|
101
106
|
pushManager.start();
|
|
102
107
|
} else {
|
|
@@ -105,13 +110,14 @@ export function syncManagerOnlineFactory(
|
|
|
105
110
|
} else {
|
|
106
111
|
if (startFirstTime) {
|
|
107
112
|
pollingManager.syncAll();
|
|
108
|
-
startFirstTime = false;
|
|
109
113
|
}
|
|
110
114
|
}
|
|
111
115
|
}
|
|
112
116
|
|
|
113
117
|
// start periodic data recording (events, impressions, telemetry).
|
|
114
118
|
submitterManager.start(!isConsentGranted(settings));
|
|
119
|
+
|
|
120
|
+
startFirstTime = false;
|
|
115
121
|
},
|
|
116
122
|
|
|
117
123
|
/**
|
|
@@ -142,11 +148,12 @@ export function syncManagerOnlineFactory(
|
|
|
142
148
|
if (!pollingManager) return;
|
|
143
149
|
|
|
144
150
|
const mySegmentsSyncTask = (pollingManager as IPollingManagerCS).add(matchingKey, readinessManager, storage);
|
|
151
|
+
if (syncEnabled && pushManager) pushManager.add(matchingKey, mySegmentsSyncTask);
|
|
145
152
|
|
|
146
153
|
if (running) {
|
|
147
154
|
if (syncEnabled) {
|
|
148
155
|
if (pushManager) {
|
|
149
|
-
if (pollingManager
|
|
156
|
+
if (pollingManager.isRunning()) {
|
|
150
157
|
// if doing polling, we must start the periodic fetch of data
|
|
151
158
|
if (storage.splits.usesSegments()) mySegmentsSyncTask.start();
|
|
152
159
|
} else {
|
|
@@ -154,7 +161,6 @@ export function syncManagerOnlineFactory(
|
|
|
154
161
|
// of segments since `syncAll` was already executed when starting the main client
|
|
155
162
|
mySegmentsSyncTask.execute();
|
|
156
163
|
}
|
|
157
|
-
pushManager.add(matchingKey, mySegmentsSyncTask);
|
|
158
164
|
} else {
|
|
159
165
|
if (storage.splits.usesSegments()) mySegmentsSyncTask.start();
|
|
160
166
|
}
|
|
@@ -36,7 +36,7 @@ export function eventTrackerFactory(
|
|
|
36
36
|
whenInit(() => {
|
|
37
37
|
// Wrap in a timeout because we don't want it to be blocking.
|
|
38
38
|
setTimeout(() => {
|
|
39
|
-
// copy of event, to avoid unexpected
|
|
39
|
+
// copy of event, to avoid unexpected behavior if modified by integrations
|
|
40
40
|
const eventDataCopy = objectAssign({}, eventData);
|
|
41
41
|
if (properties) eventDataCopy.properties = objectAssign({}, properties);
|
|
42
42
|
// integrationsManager does not throw errors (they are internally handled by each integration module)
|