@splitsoftware/splitio-commons 2.2.1-rc.1 → 2.2.1-rc.3
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 +2 -2
- package/README.md +0 -1
- package/cjs/evaluator/combiners/and.js +6 -2
- package/cjs/evaluator/combiners/ifelseif.js +6 -6
- package/cjs/evaluator/condition/index.js +5 -6
- package/cjs/evaluator/index.js +7 -7
- package/cjs/evaluator/matchers/index.js +1 -3
- package/cjs/evaluator/matchers/matcherTypes.js +0 -1
- package/cjs/evaluator/matchersTransform/index.js +0 -4
- package/cjs/evaluator/parser/index.js +2 -2
- package/cjs/evaluator/value/sanitize.js +0 -1
- package/cjs/logger/constants.js +3 -4
- package/cjs/logger/messages/debug.js +2 -3
- package/cjs/logger/messages/warn.js +1 -1
- package/cjs/sdkFactory/index.js +3 -0
- package/cjs/services/splitApi.js +4 -3
- package/cjs/storages/AbstractSplitsCacheSync.js +2 -5
- package/cjs/storages/KeyBuilder.js +0 -9
- package/cjs/storages/KeyBuilderCS.js +0 -3
- package/cjs/storages/KeyBuilderSS.js +0 -3
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +14 -9
- package/cjs/storages/inLocalStorage/index.js +1 -5
- package/cjs/storages/inLocalStorage/validateCache.js +1 -2
- package/cjs/storages/inMemory/InMemoryStorage.js +0 -3
- package/cjs/storages/inMemory/InMemoryStorageCS.js +0 -4
- package/cjs/storages/inRedis/index.js +12 -6
- package/cjs/storages/pluggable/index.js +0 -2
- package/cjs/sync/polling/fetchers/splitChangesFetcher.js +4 -51
- package/cjs/sync/polling/pollingManagerCS.js +7 -7
- package/cjs/sync/polling/syncTasks/splitsSyncTask.js +1 -1
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +2 -2
- package/cjs/sync/polling/updaters/segmentChangesUpdater.js +1 -1
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +33 -51
- package/cjs/sync/streaming/SSEHandler/index.js +0 -1
- package/cjs/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +77 -106
- package/cjs/sync/streaming/constants.js +1 -2
- package/cjs/sync/streaming/pushManager.js +16 -3
- package/cjs/sync/syncManagerOnline.js +2 -2
- package/cjs/utils/constants/index.js +2 -3
- package/esm/evaluator/combiners/and.js +6 -2
- package/esm/evaluator/combiners/ifelseif.js +7 -7
- package/esm/evaluator/condition/index.js +5 -6
- package/esm/evaluator/index.js +7 -7
- package/esm/evaluator/matchers/index.js +1 -3
- package/esm/evaluator/matchers/matcherTypes.js +0 -1
- package/esm/evaluator/matchersTransform/index.js +0 -4
- package/esm/evaluator/parser/index.js +2 -2
- package/esm/evaluator/value/sanitize.js +0 -1
- package/esm/logger/constants.js +0 -1
- package/esm/logger/messages/debug.js +2 -3
- package/esm/logger/messages/warn.js +1 -1
- package/esm/sdkFactory/index.js +4 -1
- package/esm/services/splitApi.js +4 -3
- package/esm/storages/AbstractSplitsCacheSync.js +2 -5
- package/esm/storages/KeyBuilder.js +0 -9
- package/esm/storages/KeyBuilderCS.js +0 -3
- package/esm/storages/KeyBuilderSS.js +0 -3
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +14 -9
- package/esm/storages/inLocalStorage/index.js +1 -5
- package/esm/storages/inLocalStorage/validateCache.js +1 -2
- package/esm/storages/inMemory/InMemoryStorage.js +0 -3
- package/esm/storages/inMemory/InMemoryStorageCS.js +0 -4
- package/esm/storages/inRedis/index.js +12 -6
- package/esm/storages/pluggable/index.js +0 -2
- package/esm/sync/polling/fetchers/splitChangesFetcher.js +4 -51
- package/esm/sync/polling/pollingManagerCS.js +7 -7
- package/esm/sync/polling/syncTasks/splitsSyncTask.js +1 -1
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +2 -2
- package/esm/sync/polling/updaters/segmentChangesUpdater.js +1 -1
- package/esm/sync/polling/updaters/splitChangesUpdater.js +33 -51
- package/esm/sync/streaming/SSEHandler/index.js +1 -2
- package/esm/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +73 -102
- package/esm/sync/streaming/constants.js +0 -1
- package/esm/sync/streaming/pushManager.js +19 -6
- package/esm/sync/syncManagerOnline.js +2 -2
- package/esm/utils/constants/index.js +1 -2
- package/package.json +1 -1
- package/src/dtos/types.ts +8 -32
- package/src/evaluator/Engine.ts +1 -1
- package/src/evaluator/combiners/and.ts +4 -5
- package/src/evaluator/combiners/ifelseif.ts +9 -7
- package/src/evaluator/condition/engineUtils.ts +1 -1
- package/src/evaluator/condition/index.ts +12 -12
- package/src/evaluator/index.ts +7 -7
- package/src/evaluator/matchers/index.ts +1 -3
- package/src/evaluator/matchers/matcherTypes.ts +0 -1
- package/src/evaluator/matchersTransform/index.ts +0 -3
- package/src/evaluator/parser/index.ts +3 -3
- package/src/evaluator/types.ts +2 -2
- package/src/evaluator/value/index.ts +2 -2
- package/src/evaluator/value/sanitize.ts +4 -5
- package/src/logger/constants.ts +0 -1
- package/src/logger/messages/debug.ts +2 -3
- package/src/logger/messages/warn.ts +1 -1
- package/src/sdkFactory/index.ts +4 -1
- package/src/sdkManager/index.ts +1 -1
- package/src/services/splitApi.ts +4 -3
- package/src/services/types.ts +1 -1
- package/src/storages/AbstractSplitsCacheSync.ts +3 -6
- package/src/storages/KeyBuilder.ts +0 -12
- package/src/storages/KeyBuilderCS.ts +0 -4
- package/src/storages/KeyBuilderSS.ts +0 -4
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +14 -10
- package/src/storages/inLocalStorage/index.ts +1 -5
- package/src/storages/inLocalStorage/validateCache.ts +1 -3
- package/src/storages/inMemory/InMemoryStorage.ts +0 -3
- package/src/storages/inMemory/InMemoryStorageCS.ts +0 -4
- package/src/storages/inRedis/index.ts +15 -8
- package/src/storages/pluggable/index.ts +0 -2
- package/src/storages/types.ts +2 -33
- package/src/sync/polling/fetchers/splitChangesFetcher.ts +4 -62
- package/src/sync/polling/fetchers/types.ts +0 -1
- package/src/sync/polling/pollingManagerCS.ts +7 -7
- package/src/sync/polling/syncTasks/splitsSyncTask.ts +1 -1
- package/src/sync/polling/types.ts +2 -2
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +2 -2
- package/src/sync/polling/updaters/segmentChangesUpdater.ts +1 -1
- package/src/sync/polling/updaters/splitChangesUpdater.ts +42 -61
- package/src/sync/streaming/SSEHandler/index.ts +1 -2
- package/src/sync/streaming/SSEHandler/types.ts +2 -2
- package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +68 -98
- package/src/sync/streaming/constants.ts +0 -1
- package/src/sync/streaming/parseUtils.ts +2 -2
- package/src/sync/streaming/pushManager.ts +18 -6
- package/src/sync/streaming/types.ts +2 -3
- package/src/sync/syncManagerOnline.ts +2 -2
- package/src/utils/constants/index.ts +1 -2
- package/src/utils/lang/index.ts +1 -1
- package/cjs/evaluator/matchers/rbsegment.js +0 -44
- package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +0 -117
- package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +0 -61
- package/cjs/storages/inRedis/RBSegmentsCacheInRedis.js +0 -64
- package/cjs/storages/pluggable/RBSegmentsCachePluggable.js +0 -64
- package/esm/evaluator/matchers/rbsegment.js +0 -40
- package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +0 -114
- package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +0 -58
- package/esm/storages/inRedis/RBSegmentsCacheInRedis.js +0 -61
- package/esm/storages/pluggable/RBSegmentsCachePluggable.js +0 -61
- package/src/evaluator/matchers/rbsegment.ts +0 -62
- package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +0 -136
- package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +0 -68
- package/src/storages/inRedis/RBSegmentsCacheInRedis.ts +0 -79
- package/src/storages/pluggable/RBSegmentsCachePluggable.ts +0 -76
|
@@ -11,34 +11,42 @@ import { TelemetryCacheInRedis } from './TelemetryCacheInRedis';
|
|
|
11
11
|
import { UniqueKeysCacheInRedis } from './UniqueKeysCacheInRedis';
|
|
12
12
|
import { ImpressionCountsCacheInRedis } from './ImpressionCountsCacheInRedis';
|
|
13
13
|
import { metadataBuilder } from '../utils';
|
|
14
|
-
import { RBSegmentsCacheInRedis } from './RBSegmentsCacheInRedis';
|
|
15
14
|
|
|
16
15
|
export interface InRedisStorageOptions {
|
|
17
16
|
prefix?: string
|
|
18
17
|
options?: Record<string, any>
|
|
19
18
|
}
|
|
20
19
|
|
|
20
|
+
let RD: typeof RedisAdapter | undefined;
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
// Using `require` to prevent error when bundling or importing the SDK in a .mjs file, since ioredis is a CommonJS module.
|
|
24
|
+
// Redis storage is not supported with .mjs files.
|
|
25
|
+
RD = require('./RedisAdapter').RedisAdapter;
|
|
26
|
+
} catch (error) { /* empty */ }
|
|
27
|
+
|
|
21
28
|
/**
|
|
22
29
|
* InRedis storage factory for consumer server-side SplitFactory, that uses `Ioredis` Redis client for Node.js
|
|
23
30
|
* @see {@link https://www.npmjs.com/package/ioredis}
|
|
24
31
|
*/
|
|
25
32
|
export function InRedisStorage(options: InRedisStorageOptions = {}): IStorageAsyncFactory {
|
|
26
33
|
|
|
27
|
-
// Lazy loading to prevent error when bundling or importing the SDK in a .mjs file, since ioredis is a CommonJS module.
|
|
28
|
-
// Redis storage is not supported with .mjs files.
|
|
29
|
-
const RD = require('./RedisAdapter').RedisAdapter;
|
|
30
|
-
|
|
31
34
|
const prefix = validatePrefix(options.prefix);
|
|
32
35
|
|
|
33
36
|
function InRedisStorageFactory(params: IStorageFactoryParams): IStorageAsync {
|
|
34
|
-
|
|
37
|
+
if (!RD) throw new Error('The SDK Redis storage is not available. Your runtime environment must support CommonJS (`require`) to import the ioredis dependency.');
|
|
38
|
+
|
|
39
|
+
const { onReadyFromCacheCb, onReadyCb, settings, settings: { log } } = params;
|
|
35
40
|
const metadata = metadataBuilder(settings);
|
|
36
41
|
const keys = new KeyBuilderSS(prefix, metadata);
|
|
37
|
-
const redisClient
|
|
42
|
+
const redisClient = new RD(log, options.options || {});
|
|
38
43
|
const telemetry = new TelemetryCacheInRedis(log, keys, redisClient);
|
|
39
44
|
const impressionCountsCache = new ImpressionCountsCacheInRedis(log, keys.buildImpressionsCountKey(), redisClient);
|
|
40
45
|
const uniqueKeysCache = new UniqueKeysCacheInRedis(log, keys.buildUniqueKeysKey(), redisClient);
|
|
41
46
|
|
|
47
|
+
// RedisAdapter queues operations before connection
|
|
48
|
+
onReadyFromCacheCb();
|
|
49
|
+
|
|
42
50
|
// subscription to Redis connect event in order to emit SDK_READY event on consumer mode
|
|
43
51
|
redisClient.on('connect', () => {
|
|
44
52
|
onReadyCb();
|
|
@@ -51,7 +59,6 @@ export function InRedisStorage(options: InRedisStorageOptions = {}): IStorageAsy
|
|
|
51
59
|
|
|
52
60
|
return {
|
|
53
61
|
splits: new SplitsCacheInRedis(log, keys, redisClient, settings.sync.__splitFiltersValidation),
|
|
54
|
-
rbSegments: new RBSegmentsCacheInRedis(log, keys, redisClient),
|
|
55
62
|
segments: new SegmentsCacheInRedis(log, keys, redisClient),
|
|
56
63
|
impressions: new ImpressionsCacheInRedis(log, keys.buildImpressionsKey(), redisClient, metadata),
|
|
57
64
|
impressionCounts: impressionCountsCache,
|
|
@@ -20,7 +20,6 @@ import { UniqueKeysCacheInMemory } from '../inMemory/UniqueKeysCacheInMemory';
|
|
|
20
20
|
import { UniqueKeysCacheInMemoryCS } from '../inMemory/UniqueKeysCacheInMemoryCS';
|
|
21
21
|
import { metadataBuilder } from '../utils';
|
|
22
22
|
import { LOG_PREFIX } from '../pluggable/constants';
|
|
23
|
-
import { RBSegmentsCachePluggable } from './RBSegmentsCachePluggable';
|
|
24
23
|
|
|
25
24
|
const NO_VALID_WRAPPER = 'Expecting pluggable storage `wrapper` in options, but no valid wrapper instance was provided.';
|
|
26
25
|
const NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.';
|
|
@@ -118,7 +117,6 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
|
|
|
118
117
|
|
|
119
118
|
return {
|
|
120
119
|
splits: new SplitsCachePluggable(log, keys, wrapper, settings.sync.__splitFiltersValidation),
|
|
121
|
-
rbSegments: new RBSegmentsCachePluggable(log, keys, wrapper),
|
|
122
120
|
segments: new SegmentsCachePluggable(log, keys, wrapper),
|
|
123
121
|
impressions: isPartialConsumer ? new ImpressionsCacheInMemory(impressionsQueueSize) : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
|
|
124
122
|
impressionCounts: impressionCountsCache,
|
package/src/storages/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import SplitIO from '../../types/splitio';
|
|
2
|
-
import { MaybeThenable, ISplit,
|
|
2
|
+
import { MaybeThenable, ISplit, IMySegmentsResponse } from '../dtos/types';
|
|
3
3
|
import { MySegmentsData } from '../sync/polling/types';
|
|
4
4
|
import { EventDataType, HttpErrors, HttpLatencies, ImpressionDataType, LastSync, Method, MethodExceptions, MethodLatencies, MultiMethodExceptions, MultiMethodLatencies, MultiConfigs, OperationType, StoredEventWithMetadata, StoredImpressionWithMetadata, StreamingEvent, UniqueKeysPayloadCs, UniqueKeysPayloadSs, TelemetryUsageStatsPayload, UpdatesFromSSEEnum } from '../sync/submitters/types';
|
|
5
5
|
import { ISettings } from '../types';
|
|
@@ -221,34 +221,6 @@ export interface ISplitsCacheAsync extends ISplitsCacheBase {
|
|
|
221
221
|
getNamesByFlagSets(flagSets: string[]): Promise<Set<string>[]>
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
/** Rule-Based Segments cache */
|
|
225
|
-
|
|
226
|
-
export interface IRBSegmentsCacheBase {
|
|
227
|
-
update(toAdd: IRBSegment[], toRemove: IRBSegment[], changeNumber: number): MaybeThenable<boolean>,
|
|
228
|
-
get(name: string): MaybeThenable<IRBSegment | null>,
|
|
229
|
-
getChangeNumber(): MaybeThenable<number>,
|
|
230
|
-
clear(): MaybeThenable<boolean | void>,
|
|
231
|
-
contains(names: Set<string>): MaybeThenable<boolean>,
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
export interface IRBSegmentsCacheSync extends IRBSegmentsCacheBase {
|
|
235
|
-
update(toAdd: IRBSegment[], toRemove: IRBSegment[], changeNumber: number): boolean,
|
|
236
|
-
get(name: string): IRBSegment | null,
|
|
237
|
-
getChangeNumber(): number,
|
|
238
|
-
clear(): void,
|
|
239
|
-
contains(names: Set<string>): boolean,
|
|
240
|
-
// Used only for smart pausing in client-side standalone. Returns true if the storage contains a RBSegment using segments or large segments matchers
|
|
241
|
-
usesSegments(): boolean,
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
export interface IRBSegmentsCacheAsync extends IRBSegmentsCacheBase {
|
|
245
|
-
update(toAdd: IRBSegment[], toRemove: IRBSegment[], changeNumber: number): Promise<boolean>,
|
|
246
|
-
get(name: string): Promise<IRBSegment | null>,
|
|
247
|
-
getChangeNumber(): Promise<number>,
|
|
248
|
-
clear(): Promise<boolean | void>,
|
|
249
|
-
contains(names: Set<string>): Promise<boolean>,
|
|
250
|
-
}
|
|
251
|
-
|
|
252
224
|
/** Segments cache */
|
|
253
225
|
|
|
254
226
|
export interface ISegmentsCacheBase {
|
|
@@ -447,7 +419,6 @@ export interface ITelemetryCacheAsync extends ITelemetryEvaluationProducerAsync,
|
|
|
447
419
|
|
|
448
420
|
export interface IStorageBase<
|
|
449
421
|
TSplitsCache extends ISplitsCacheBase = ISplitsCacheBase,
|
|
450
|
-
TRBSegmentsCache extends IRBSegmentsCacheBase = IRBSegmentsCacheBase,
|
|
451
422
|
TSegmentsCache extends ISegmentsCacheBase = ISegmentsCacheBase,
|
|
452
423
|
TImpressionsCache extends IImpressionsCacheBase = IImpressionsCacheBase,
|
|
453
424
|
TImpressionsCountCache extends IImpressionCountsCacheBase = IImpressionCountsCacheBase,
|
|
@@ -456,7 +427,6 @@ export interface IStorageBase<
|
|
|
456
427
|
TUniqueKeysCache extends IUniqueKeysCacheBase = IUniqueKeysCacheBase
|
|
457
428
|
> {
|
|
458
429
|
splits: TSplitsCache,
|
|
459
|
-
rbSegments: TRBSegmentsCache,
|
|
460
430
|
segments: TSegmentsCache,
|
|
461
431
|
impressions: TImpressionsCache,
|
|
462
432
|
impressionCounts: TImpressionsCountCache,
|
|
@@ -469,7 +439,6 @@ export interface IStorageBase<
|
|
|
469
439
|
|
|
470
440
|
export interface IStorageSync extends IStorageBase<
|
|
471
441
|
ISplitsCacheSync,
|
|
472
|
-
IRBSegmentsCacheSync,
|
|
473
442
|
ISegmentsCacheSync,
|
|
474
443
|
IImpressionsCacheSync,
|
|
475
444
|
IImpressionCountsCacheSync,
|
|
@@ -484,7 +453,6 @@ export interface IStorageSync extends IStorageBase<
|
|
|
484
453
|
|
|
485
454
|
export interface IStorageAsync extends IStorageBase<
|
|
486
455
|
ISplitsCacheAsync,
|
|
487
|
-
IRBSegmentsCacheAsync,
|
|
488
456
|
ISegmentsCacheAsync,
|
|
489
457
|
IImpressionsCacheAsync | IImpressionsCacheSync,
|
|
490
458
|
IImpressionCountsCacheBase,
|
|
@@ -504,6 +472,7 @@ export interface IStorageFactoryParams {
|
|
|
504
472
|
* It is meant for emitting SDK_READY event in consumer mode, and waiting before using the storage in the synchronizer.
|
|
505
473
|
*/
|
|
506
474
|
onReadyCb: (error?: any) => void,
|
|
475
|
+
onReadyFromCacheCb: () => void,
|
|
507
476
|
}
|
|
508
477
|
|
|
509
478
|
|
|
@@ -1,82 +1,24 @@
|
|
|
1
|
-
import { ISettings } from '../../../types';
|
|
2
|
-
import { ISplitChangesResponse } from '../../../dtos/types';
|
|
3
1
|
import { IFetchSplitChanges, IResponse } from '../../../services/types';
|
|
4
|
-
import { IStorageBase } from '../../../storages/types';
|
|
5
|
-
import { FLAG_SPEC_VERSION } from '../../../utils/constants';
|
|
6
|
-
import { base } from '../../../utils/settingsValidation';
|
|
7
2
|
import { ISplitChangesFetcher } from './types';
|
|
8
|
-
import { LOG_PREFIX_SYNC_SPLITS } from '../../../logger/constants';
|
|
9
|
-
|
|
10
|
-
const PROXY_CHECK_INTERVAL_MILLIS_CS = 60 * 60 * 1000; // 1 hour in Client Side
|
|
11
|
-
const PROXY_CHECK_INTERVAL_MILLIS_SS = 24 * PROXY_CHECK_INTERVAL_MILLIS_CS; // 24 hours in Server Side
|
|
12
|
-
|
|
13
|
-
function sdkEndpointOverriden(settings: ISettings) {
|
|
14
|
-
return settings.urls.sdk !== base.urls.sdk;
|
|
15
|
-
}
|
|
16
3
|
|
|
17
4
|
/**
|
|
18
5
|
* Factory of SplitChanges fetcher.
|
|
19
6
|
* SplitChanges fetcher is a wrapper around `splitChanges` API service that parses the response and handle errors.
|
|
20
7
|
*/
|
|
21
|
-
|
|
22
|
-
export function splitChangesFetcherFactory(fetchSplitChanges: IFetchSplitChanges, settings: ISettings, storage: Pick<IStorageBase, 'splits' | 'rbSegments'>): ISplitChangesFetcher {
|
|
23
|
-
|
|
24
|
-
const log = settings.log;
|
|
25
|
-
const PROXY_CHECK_INTERVAL_MILLIS = settings.core.key !== undefined ? PROXY_CHECK_INTERVAL_MILLIS_CS : PROXY_CHECK_INTERVAL_MILLIS_SS;
|
|
26
|
-
let lastProxyCheckTimestamp: number | undefined;
|
|
8
|
+
export function splitChangesFetcherFactory(fetchSplitChanges: IFetchSplitChanges): ISplitChangesFetcher {
|
|
27
9
|
|
|
28
10
|
return function splitChangesFetcher(
|
|
29
11
|
since: number,
|
|
30
12
|
noCache?: boolean,
|
|
31
13
|
till?: number,
|
|
32
|
-
rbSince?: number,
|
|
33
14
|
// Optional decorator for `fetchSplitChanges` promise, such as timeout or time tracker
|
|
34
15
|
decorator?: (promise: Promise<IResponse>) => Promise<IResponse>
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
// Recheck proxy
|
|
38
|
-
if (lastProxyCheckTimestamp && (Date.now() - lastProxyCheckTimestamp) > PROXY_CHECK_INTERVAL_MILLIS) {
|
|
39
|
-
settings.sync.flagSpecVersion = FLAG_SPEC_VERSION;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
let splitsPromise = fetchSplitChanges(since, noCache, till, settings.sync.flagSpecVersion === FLAG_SPEC_VERSION ? rbSince : undefined)
|
|
43
|
-
// Handle proxy error with spec 1.3
|
|
44
|
-
.catch((err) => {
|
|
45
|
-
if (err.statusCode === 400 && sdkEndpointOverriden(settings) && settings.sync.flagSpecVersion === FLAG_SPEC_VERSION) {
|
|
46
|
-
log.error(LOG_PREFIX_SYNC_SPLITS + 'Proxy error detected. If you are using Split Proxy, please upgrade to latest version');
|
|
47
|
-
lastProxyCheckTimestamp = Date.now();
|
|
48
|
-
settings.sync.flagSpecVersion = '1.2'; // fallback to 1.2 spec
|
|
49
|
-
return fetchSplitChanges(since, noCache, till); // retry request without rbSince
|
|
50
|
-
}
|
|
51
|
-
throw err;
|
|
52
|
-
});
|
|
16
|
+
) {
|
|
53
17
|
|
|
18
|
+
let splitsPromise = fetchSplitChanges(since, noCache, till);
|
|
54
19
|
if (decorator) splitsPromise = decorator(splitsPromise);
|
|
55
20
|
|
|
56
|
-
return splitsPromise
|
|
57
|
-
.then(resp => resp.json())
|
|
58
|
-
.then(data => {
|
|
59
|
-
// Using flag spec version 1.2
|
|
60
|
-
if (data.splits) {
|
|
61
|
-
return {
|
|
62
|
-
ff: {
|
|
63
|
-
d: data.splits,
|
|
64
|
-
s: data.since,
|
|
65
|
-
t: data.till
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Proxy recovery
|
|
71
|
-
if (lastProxyCheckTimestamp) {
|
|
72
|
-
log.info(LOG_PREFIX_SYNC_SPLITS + 'Proxy error recovered');
|
|
73
|
-
lastProxyCheckTimestamp = undefined;
|
|
74
|
-
return Promise.all([storage.splits.clear(), storage.rbSegments.clear()])
|
|
75
|
-
.then(() => splitChangesFetcher(storage.splits.getChangeNumber() as number, undefined, undefined, storage.rbSegments.getChangeNumber() as number));
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return data;
|
|
79
|
-
});
|
|
21
|
+
return splitsPromise.then(resp => resp.json());
|
|
80
22
|
};
|
|
81
23
|
|
|
82
24
|
}
|
|
@@ -43,10 +43,10 @@ export function pollingManagerCSFactory(
|
|
|
43
43
|
// smart pausing
|
|
44
44
|
readiness.splits.on(SDK_SPLITS_ARRIVED, () => {
|
|
45
45
|
if (!splitsSyncTask.isRunning()) return; // noop if not doing polling
|
|
46
|
-
const
|
|
47
|
-
if (
|
|
48
|
-
log.info(POLLING_SMART_PAUSING, [
|
|
49
|
-
if (
|
|
46
|
+
const splitsHaveSegments = storage.splits.usesSegments();
|
|
47
|
+
if (splitsHaveSegments !== mySegmentsSyncTask.isRunning()) {
|
|
48
|
+
log.info(POLLING_SMART_PAUSING, [splitsHaveSegments ? 'ON' : 'OFF']);
|
|
49
|
+
if (splitsHaveSegments) {
|
|
50
50
|
startMySegmentsSyncTasks();
|
|
51
51
|
} else {
|
|
52
52
|
stopMySegmentsSyncTasks();
|
|
@@ -59,9 +59,9 @@ export function pollingManagerCSFactory(
|
|
|
59
59
|
|
|
60
60
|
// smart ready
|
|
61
61
|
function smartReady() {
|
|
62
|
-
if (!readiness.isReady() && !storage.splits.usesSegments()
|
|
62
|
+
if (!readiness.isReady() && !storage.splits.usesSegments()) readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
|
|
63
63
|
}
|
|
64
|
-
if (!storage.splits.usesSegments()
|
|
64
|
+
if (!storage.splits.usesSegments()) setTimeout(smartReady, 0);
|
|
65
65
|
else readiness.splits.once(SDK_SPLITS_ARRIVED, smartReady);
|
|
66
66
|
|
|
67
67
|
mySegmentsSyncTasks[matchingKey] = mySegmentsSyncTask;
|
|
@@ -77,7 +77,7 @@ export function pollingManagerCSFactory(
|
|
|
77
77
|
log.info(POLLING_START);
|
|
78
78
|
|
|
79
79
|
splitsSyncTask.start();
|
|
80
|
-
if (storage.splits.usesSegments()
|
|
80
|
+
if (storage.splits.usesSegments()) startMySegmentsSyncTasks();
|
|
81
81
|
},
|
|
82
82
|
|
|
83
83
|
// Stop periodic fetching (polling)
|
|
@@ -21,7 +21,7 @@ export function splitsSyncTaskFactory(
|
|
|
21
21
|
settings.log,
|
|
22
22
|
splitChangesUpdaterFactory(
|
|
23
23
|
settings.log,
|
|
24
|
-
splitChangesFetcherFactory(fetchSplitChanges
|
|
24
|
+
splitChangesFetcherFactory(fetchSplitChanges),
|
|
25
25
|
storage,
|
|
26
26
|
settings.sync.__splitFiltersValidation,
|
|
27
27
|
readiness.splits,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ISplit } from '../../dtos/types';
|
|
2
2
|
import { IReadinessManager } from '../../readiness/types';
|
|
3
3
|
import { IStorageSync } from '../../storages/types';
|
|
4
4
|
import { MEMBERSHIPS_LS_UPDATE, MEMBERSHIPS_MS_UPDATE } from '../streaming/types';
|
|
5
5
|
import { ITask, ISyncTask } from '../types';
|
|
6
6
|
|
|
7
|
-
export interface ISplitsSyncTask extends ISyncTask<[noCache?: boolean, till?: number, splitUpdateNotification?: { payload: ISplit
|
|
7
|
+
export interface ISplitsSyncTask extends ISyncTask<[noCache?: boolean, till?: number, splitUpdateNotification?: { payload: ISplit, changeNumber: number }], boolean> { }
|
|
8
8
|
|
|
9
9
|
export interface ISegmentsSyncTask extends ISyncTask<[fetchOnlyNew?: boolean, segmentName?: string, noCache?: boolean, till?: number], boolean> { }
|
|
10
10
|
|
|
@@ -27,7 +27,7 @@ export function mySegmentsUpdaterFactory(
|
|
|
27
27
|
matchingKey: string
|
|
28
28
|
): IMySegmentsUpdater {
|
|
29
29
|
|
|
30
|
-
const { splits,
|
|
30
|
+
const { splits, segments, largeSegments } = storage;
|
|
31
31
|
let readyOnAlreadyExistentState = true;
|
|
32
32
|
let startingUp = true;
|
|
33
33
|
|
|
@@ -51,7 +51,7 @@ export function mySegmentsUpdaterFactory(
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
// Notify update if required
|
|
54
|
-
if (
|
|
54
|
+
if (splits.usesSegments() && (shouldNotifyUpdate || readyOnAlreadyExistentState)) {
|
|
55
55
|
readyOnAlreadyExistentState = false;
|
|
56
56
|
segmentsEventEmitter.emit(SDK_SEGMENTS_ARRIVED);
|
|
57
57
|
}
|
|
@@ -51,7 +51,7 @@ export function segmentChangesUpdaterFactory(
|
|
|
51
51
|
* Returned promise will not be rejected.
|
|
52
52
|
*
|
|
53
53
|
* @param fetchOnlyNew - if true, only fetch the segments that not exists, i.e., which `changeNumber` is equal to -1.
|
|
54
|
-
* This param is used by SplitUpdateWorker on server-side SDK, to fetch new registered segments on SPLIT_UPDATE
|
|
54
|
+
* This param is used by SplitUpdateWorker on server-side SDK, to fetch new registered segments on SPLIT_UPDATE notifications.
|
|
55
55
|
* @param segmentName - segment name to fetch. By passing `undefined` it fetches the list of segments registered at the storage
|
|
56
56
|
* @param noCache - true to revalidate data to fetch on a SEGMENT_UPDATE notifications.
|
|
57
57
|
* @param till - till target for the provided segmentName, for CDN bypass.
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import { ISegmentsCacheBase, IStorageBase } from '../../../storages/types';
|
|
2
2
|
import { ISplitChangesFetcher } from '../fetchers/types';
|
|
3
|
-
import {
|
|
3
|
+
import { ISplit, ISplitChangesResponse, ISplitFiltersValidation } from '../../../dtos/types';
|
|
4
4
|
import { ISplitsEventEmitter } from '../../../readiness/types';
|
|
5
5
|
import { timeout } from '../../../utils/promise/timeout';
|
|
6
6
|
import { SDK_SPLITS_ARRIVED } from '../../../readiness/constants';
|
|
7
7
|
import { ILogger } from '../../../logger/types';
|
|
8
|
-
import { SYNC_SPLITS_FETCH, SYNC_SPLITS_UPDATE,
|
|
8
|
+
import { SYNC_SPLITS_FETCH, SYNC_SPLITS_UPDATE, SYNC_SPLITS_FETCH_FAILS, SYNC_SPLITS_FETCH_RETRY } from '../../../logger/constants';
|
|
9
9
|
import { startsWith } from '../../../utils/lang';
|
|
10
|
-
import {
|
|
10
|
+
import { IN_SEGMENT } from '../../../utils/constants';
|
|
11
11
|
import { setToArray } from '../../../utils/lang/sets';
|
|
12
|
-
import { SPLIT_UPDATE } from '../../streaming/constants';
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
type SplitChangesUpdater = (noCache?: boolean, till?: number, instantUpdate?: InstantUpdate) => Promise<boolean>
|
|
13
|
+
type ISplitChangesUpdater = (noCache?: boolean, till?: number, splitUpdateNotification?: { payload: ISplit, changeNumber: number }) => Promise<boolean>
|
|
16
14
|
|
|
17
15
|
// Checks that all registered segments have been fetched (changeNumber !== -1 for every segment).
|
|
18
16
|
// Returns a promise that could be rejected.
|
|
@@ -29,24 +27,24 @@ function checkAllSegmentsExist(segments: ISegmentsCacheBase): Promise<boolean> {
|
|
|
29
27
|
* Collect segments from a raw split definition.
|
|
30
28
|
* Exported for testing purposes.
|
|
31
29
|
*/
|
|
32
|
-
export function parseSegments(
|
|
33
|
-
|
|
34
|
-
const segments = new Set<string>(excluded && excluded.segments);
|
|
30
|
+
export function parseSegments({ conditions }: ISplit): Set<string> {
|
|
31
|
+
let segments = new Set<string>();
|
|
35
32
|
|
|
36
33
|
for (let i = 0; i < conditions.length; i++) {
|
|
37
34
|
const matchers = conditions[i].matcherGroup.matchers;
|
|
38
35
|
|
|
39
36
|
matchers.forEach(matcher => {
|
|
40
|
-
if (matcher.matcherType ===
|
|
37
|
+
if (matcher.matcherType === IN_SEGMENT) segments.add(matcher.userDefinedSegmentMatcherData.segmentName);
|
|
41
38
|
});
|
|
42
39
|
}
|
|
43
40
|
|
|
44
41
|
return segments;
|
|
45
42
|
}
|
|
46
43
|
|
|
47
|
-
interface ISplitMutations
|
|
48
|
-
added:
|
|
49
|
-
removed:
|
|
44
|
+
interface ISplitMutations {
|
|
45
|
+
added: ISplit[],
|
|
46
|
+
removed: ISplit[],
|
|
47
|
+
segments: string[]
|
|
50
48
|
}
|
|
51
49
|
|
|
52
50
|
/**
|
|
@@ -75,21 +73,25 @@ function matchFilters(featureFlag: ISplit, filters: ISplitFiltersValidation) {
|
|
|
75
73
|
* i.e., an object with added splits, removed splits and used segments.
|
|
76
74
|
* Exported for testing purposes.
|
|
77
75
|
*/
|
|
78
|
-
export function
|
|
76
|
+
export function computeSplitsMutation(entries: ISplit[], filters: ISplitFiltersValidation): ISplitMutations {
|
|
77
|
+
const segments = new Set<string>();
|
|
78
|
+
const computed = entries.reduce((accum, split) => {
|
|
79
|
+
if (split.status === 'ACTIVE' && matchFilters(split, filters)) {
|
|
80
|
+
accum.added.push(split);
|
|
79
81
|
|
|
80
|
-
|
|
81
|
-
if (ruleEntity.status === 'ACTIVE' && (!filters || matchFilters(ruleEntity as ISplit, filters))) {
|
|
82
|
-
accum.added.push(ruleEntity);
|
|
83
|
-
|
|
84
|
-
parseSegments(ruleEntity).forEach((segmentName: string) => {
|
|
82
|
+
parseSegments(split).forEach((segmentName: string) => {
|
|
85
83
|
segments.add(segmentName);
|
|
86
84
|
});
|
|
87
85
|
} else {
|
|
88
|
-
accum.removed.push(
|
|
86
|
+
accum.removed.push(split);
|
|
89
87
|
}
|
|
90
88
|
|
|
91
89
|
return accum;
|
|
92
|
-
}, { added: [], removed: [] } as ISplitMutations
|
|
90
|
+
}, { added: [], removed: [], segments: [] } as ISplitMutations);
|
|
91
|
+
|
|
92
|
+
computed.segments = setToArray(segments);
|
|
93
|
+
|
|
94
|
+
return computed;
|
|
93
95
|
}
|
|
94
96
|
|
|
95
97
|
/**
|
|
@@ -109,14 +111,14 @@ export function computeMutation<T extends ISplit | IRBSegment>(rules: Array<T>,
|
|
|
109
111
|
export function splitChangesUpdaterFactory(
|
|
110
112
|
log: ILogger,
|
|
111
113
|
splitChangesFetcher: ISplitChangesFetcher,
|
|
112
|
-
storage: Pick<IStorageBase, 'splits' | '
|
|
114
|
+
storage: Pick<IStorageBase, 'splits' | 'segments'>,
|
|
113
115
|
splitFiltersValidation: ISplitFiltersValidation,
|
|
114
116
|
splitsEventEmitter?: ISplitsEventEmitter,
|
|
115
117
|
requestTimeoutBeforeReady: number = 0,
|
|
116
118
|
retriesOnFailureBeforeReady: number = 0,
|
|
117
119
|
isClientSide?: boolean
|
|
118
|
-
):
|
|
119
|
-
const { splits,
|
|
120
|
+
): ISplitChangesUpdater {
|
|
121
|
+
const { splits, segments } = storage;
|
|
120
122
|
|
|
121
123
|
let startingUp = true;
|
|
122
124
|
|
|
@@ -133,53 +135,32 @@ export function splitChangesUpdaterFactory(
|
|
|
133
135
|
* @param noCache - true to revalidate data to fetch
|
|
134
136
|
* @param till - query param to bypass CDN requests
|
|
135
137
|
*/
|
|
136
|
-
return function splitChangesUpdater(noCache?: boolean, till?: number,
|
|
138
|
+
return function splitChangesUpdater(noCache?: boolean, till?: number, splitUpdateNotification?: { payload: ISplit, changeNumber: number }) {
|
|
137
139
|
|
|
138
140
|
/**
|
|
139
141
|
* @param since - current changeNumber at splitsCache
|
|
140
142
|
* @param retry - current number of retry attempts
|
|
141
143
|
*/
|
|
142
|
-
function _splitChangesUpdater(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
instantUpdate.type === SPLIT_UPDATE ?
|
|
148
|
-
// IFFU edge case: a change to a flag that adds an IN_RULE_BASED_SEGMENT matcher that is not present yet
|
|
149
|
-
Promise.resolve(rbSegments.contains(parseSegments(instantUpdate.payload, IN_RULE_BASED_SEGMENT))).then((contains) => {
|
|
150
|
-
return contains ?
|
|
151
|
-
{ ff: { d: [instantUpdate.payload as ISplit], t: instantUpdate.changeNumber } } :
|
|
152
|
-
splitChangesFetcher(since, noCache, till, rbSince, _promiseDecorator);
|
|
153
|
-
}) :
|
|
154
|
-
{ rbs: { d: [instantUpdate.payload as IRBSegment], t: instantUpdate.changeNumber } } :
|
|
155
|
-
splitChangesFetcher(since, noCache, till, rbSince, _promiseDecorator)
|
|
144
|
+
function _splitChangesUpdater(since: number, retry = 0): Promise<boolean> {
|
|
145
|
+
log.debug(SYNC_SPLITS_FETCH, [since]);
|
|
146
|
+
return Promise.resolve(splitUpdateNotification ?
|
|
147
|
+
{ splits: [splitUpdateNotification.payload], till: splitUpdateNotification.changeNumber } :
|
|
148
|
+
splitChangesFetcher(since, noCache, till, _promiseDecorator)
|
|
156
149
|
)
|
|
157
150
|
.then((splitChanges: ISplitChangesResponse) => {
|
|
158
151
|
startingUp = false;
|
|
159
152
|
|
|
160
|
-
const
|
|
153
|
+
const mutation = computeSplitsMutation(splitChanges.splits, splitFiltersValidation);
|
|
161
154
|
|
|
162
|
-
|
|
163
|
-
if (splitChanges.ff) {
|
|
164
|
-
const { added, removed } = computeMutation(splitChanges.ff.d, usedSegments, splitFiltersValidation);
|
|
165
|
-
log.debug(SYNC_SPLITS_UPDATE, [added.length, removed.length]);
|
|
166
|
-
ffUpdate = splits.update(added, removed, splitChanges.ff.t);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
let rbsUpdate: MaybeThenable<boolean> = false;
|
|
170
|
-
if (splitChanges.rbs) {
|
|
171
|
-
const { added, removed } = computeMutation(splitChanges.rbs.d, usedSegments);
|
|
172
|
-
log.debug(SYNC_RBS_UPDATE, [added.length, removed.length]);
|
|
173
|
-
rbsUpdate = rbSegments.update(added, removed, splitChanges.rbs.t);
|
|
174
|
-
}
|
|
155
|
+
log.debug(SYNC_SPLITS_UPDATE, [mutation.added.length, mutation.removed.length, mutation.segments.length]);
|
|
175
156
|
|
|
176
|
-
return Promise.all([
|
|
177
|
-
|
|
178
|
-
segments.registerSegments(
|
|
179
|
-
]).then(([
|
|
157
|
+
return Promise.all([
|
|
158
|
+
splits.update(mutation.added, mutation.removed, splitChanges.till),
|
|
159
|
+
segments.registerSegments(mutation.segments)
|
|
160
|
+
]).then(([isThereUpdate]) => {
|
|
180
161
|
if (splitsEventEmitter) {
|
|
181
162
|
// To emit SDK_SPLITS_ARRIVED for server-side SDK, we must check that all registered segments have been fetched
|
|
182
|
-
return Promise.resolve(!splitsEventEmitter.splitsArrived || (
|
|
163
|
+
return Promise.resolve(!splitsEventEmitter.splitsArrived || (since !== splitChanges.till && isThereUpdate && (isClientSide || checkAllSegmentsExist(segments))))
|
|
183
164
|
.catch(() => false /** noop. just to handle a possible `checkAllSegmentsExist` rejection, before emitting SDK event */)
|
|
184
165
|
.then(emitSplitsArrivedEvent => {
|
|
185
166
|
// emit SDK events
|
|
@@ -196,7 +177,7 @@ export function splitChangesUpdaterFactory(
|
|
|
196
177
|
if (startingUp && retriesOnFailureBeforeReady > retry) {
|
|
197
178
|
retry += 1;
|
|
198
179
|
log.info(SYNC_SPLITS_FETCH_RETRY, [retry, error]);
|
|
199
|
-
return _splitChangesUpdater(
|
|
180
|
+
return _splitChangesUpdater(since, retry);
|
|
200
181
|
} else {
|
|
201
182
|
startingUp = false;
|
|
202
183
|
}
|
|
@@ -204,7 +185,7 @@ export function splitChangesUpdaterFactory(
|
|
|
204
185
|
});
|
|
205
186
|
}
|
|
206
187
|
|
|
207
|
-
// `getChangeNumber` never rejects or throws error
|
|
208
|
-
return
|
|
188
|
+
let sincePromise = Promise.resolve(splits.getChangeNumber()); // `getChangeNumber` never rejects or throws error
|
|
189
|
+
return sincePromise.then(_splitChangesUpdater);
|
|
209
190
|
};
|
|
210
191
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { errorParser, messageParser } from './NotificationParser';
|
|
2
2
|
import { notificationKeeperFactory } from './NotificationKeeper';
|
|
3
|
-
import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE
|
|
3
|
+
import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE } from '../constants';
|
|
4
4
|
import { IPushEventEmitter } from '../types';
|
|
5
5
|
import { ISseEventHandler } from '../SSEClient/types';
|
|
6
6
|
import { INotificationError, INotificationMessage } from './types';
|
|
@@ -84,7 +84,6 @@ export function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventEmitter,
|
|
|
84
84
|
case MEMBERSHIPS_MS_UPDATE:
|
|
85
85
|
case MEMBERSHIPS_LS_UPDATE:
|
|
86
86
|
case SPLIT_KILL:
|
|
87
|
-
case RB_SEGMENT_UPDATE:
|
|
88
87
|
pushEmitter.emit(parsedData.type, parsedData);
|
|
89
88
|
break;
|
|
90
89
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ControlType } from '../constants';
|
|
2
|
-
import { SEGMENT_UPDATE, SPLIT_UPDATE, SPLIT_KILL, CONTROL, OCCUPANCY, MEMBERSHIPS_LS_UPDATE, MEMBERSHIPS_MS_UPDATE
|
|
2
|
+
import { SEGMENT_UPDATE, SPLIT_UPDATE, SPLIT_KILL, CONTROL, OCCUPANCY, MEMBERSHIPS_LS_UPDATE, MEMBERSHIPS_MS_UPDATE } from '../types';
|
|
3
3
|
|
|
4
4
|
export enum Compression {
|
|
5
5
|
None = 0,
|
|
@@ -42,7 +42,7 @@ export interface ISegmentUpdateData {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
export interface ISplitUpdateData {
|
|
45
|
-
type: SPLIT_UPDATE
|
|
45
|
+
type: SPLIT_UPDATE,
|
|
46
46
|
changeNumber: number,
|
|
47
47
|
pcn?: number,
|
|
48
48
|
d?: string,
|