Skip to content

Commit 1453af8

Browse files
Merge pull request #286 from splitio/development
Release v1.12.1
2 parents e35cb7c + b16f009 commit 1453af8

30 files changed

+202
-137
lines changed

CHANGES.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
1.12.1 (December 12, 2023)
2+
- Updated PluggableStorage, for producer mode, and LocalStorage, for standalone mode, to clear the storage before initiating the synchronization process if it was previously synchronized with a different SDK key (i.e., a different environment) or different Split Filter criteria.
3+
- Bugfixing - Fixed an issue when tracking telemetry latencies for the new `getTreatmentsByFlagSet(s)` methods in Redis and Pluggable storages, which was causing the SDK to not track those stats.
4+
15
1.12.0 (December 4, 2023)
26
- Added support for Flag Sets in "consumer" and "partial consumer" modes for Pluggable and Redis storages.
37
- Updated evaluation flow to log a warning when using flag sets that don't contain cached feature flags.

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@splitsoftware/splitio-commons",
3-
"version": "1.12.0",
3+
"version": "1.12.1",
44
"description": "Split Javascript SDK common components",
55
"main": "cjs/index.js",
66
"module": "esm/index.js",

src/sdkClient/client.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { IEvaluationResult } from '../evaluator/types';
99
import { SplitIO, ImpressionDTO } from '../types';
1010
import { IMPRESSION, IMPRESSION_QUEUEING } from '../logger/constants';
1111
import { ISdkFactoryContext } from '../sdkFactory/types';
12-
import { isStorageSync } from '../trackers/impressionObserver/utils';
12+
import { isConsumerMode } from '../utils/settingsValidation/mode';
1313
import { Method } from '../sync/submitters/types';
1414

1515
const treatmentNotReady = { treatment: CONTROL, label: SDK_NOT_READY };
@@ -28,6 +28,7 @@ function treatmentsNotReady(featureFlagNames: string[]) {
2828
export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | SplitIO.IAsyncClient {
2929
const { sdkReadinessManager: { readinessManager }, storage, settings, impressionsTracker, eventTracker, telemetryTracker } = params;
3030
const { log, mode } = settings;
31+
const isAsync = isConsumerMode(mode);
3132

3233
function getTreatment(key: SplitIO.SplitKey, featureFlagName: string, attributes: SplitIO.Attributes | undefined, withConfig = false, methodName = GET_TREATMENT) {
3334
const stopTelemetryTracker = telemetryTracker.trackEval(withConfig ? TREATMENT_WITH_CONFIG : TREATMENT);
@@ -43,9 +44,9 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
4344

4445
const evaluation = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
4546
evaluateFeature(log, key, featureFlagName, attributes, storage) :
46-
isStorageSync(settings) ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
47-
treatmentNotReady :
48-
Promise.resolve(treatmentNotReady); // Promisify if async
47+
isAsync ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
48+
Promise.resolve(treatmentNotReady) :
49+
treatmentNotReady;
4950

5051
return thenable(evaluation) ? evaluation.then((res) => wrapUp(res)) : wrapUp(evaluation);
5152
}
@@ -71,9 +72,9 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
7172

7273
const evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
7374
evaluateFeatures(log, key, featureFlagNames, attributes, storage) :
74-
isStorageSync(settings) ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
75-
treatmentsNotReady(featureFlagNames) :
76-
Promise.resolve(treatmentsNotReady(featureFlagNames)); // Promisify if async
75+
isAsync ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
76+
Promise.resolve(treatmentsNotReady(featureFlagNames)) :
77+
treatmentsNotReady(featureFlagNames);
7778

7879
return thenable(evaluations) ? evaluations.then((res) => wrapUp(res)) : wrapUp(evaluations);
7980
}
@@ -100,7 +101,9 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
100101

101102
const evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
102103
evaluateFeaturesByFlagSets(log, key, flagSetNames, attributes, storage, methodName) :
103-
isStorageSync(settings) ? {} : Promise.resolve({}); // Promisify if async
104+
isAsync ?
105+
Promise.resolve({}) :
106+
{};
104107

105108
return thenable(evaluations) ? evaluations.then((res) => wrapUp(res)) : wrapUp(evaluations);
106109
}

src/sdkClient/clientInputValidation.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { CONTROL, CONTROL_WITH_CONFIG, GET_TREATMENT, GET_TREATMENTS, GET_TREATM
1616
import { IReadinessManager } from '../readiness/types';
1717
import { MaybeThenable } from '../dtos/types';
1818
import { ISettings, SplitIO } from '../types';
19-
import { isStorageSync } from '../trackers/impressionObserver/utils';
19+
import { isConsumerMode } from '../utils/settingsValidation/mode';
2020
import { validateFlagSets } from '../utils/settingsValidation/splitFilters';
2121

2222
/**
@@ -25,8 +25,8 @@ import { validateFlagSets } from '../utils/settingsValidation/splitFilters';
2525
*/
2626
export function clientInputValidationDecorator<TClient extends SplitIO.IClient | SplitIO.IAsyncClient>(settings: ISettings, client: TClient, readinessManager: IReadinessManager): TClient {
2727

28-
const log = settings.log;
29-
const isSync = isStorageSync(settings);
28+
const { log, mode } = settings;
29+
const isAsync = isConsumerMode(mode);
3030

3131
/**
3232
* Avoid repeating this validations code
@@ -59,7 +59,7 @@ export function clientInputValidationDecorator<TClient extends SplitIO.IClient |
5959
}
6060

6161
function wrapResult<T>(value: T): MaybeThenable<T> {
62-
return isSync ? value : Promise.resolve(value);
62+
return isAsync ? Promise.resolve(value) : value;
6363
}
6464

6565
function getTreatment(maybeKey: SplitIO.SplitKey, maybeFeatureFlagName: string, maybeAttributes?: SplitIO.Attributes) {
@@ -159,7 +159,7 @@ export function clientInputValidationDecorator<TClient extends SplitIO.IClient |
159159
if (isNotDestroyed && key && tt && event && eventValue !== false && properties !== false) { // @ts-expect-error
160160
return client.track(key, tt, event, eventValue, properties, size);
161161
} else {
162-
return isSync ? false : Promise.resolve(false);
162+
return isAsync ? Promise.resolve(false) : false;
163163
}
164164
}
165165

src/sdkManager/index.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { ISplitsCacheAsync, ISplitsCacheSync } from '../storages/types';
66
import { ISdkReadinessManager } from '../readiness/types';
77
import { ISplit } from '../dtos/types';
88
import { ISettings, SplitIO } from '../types';
9-
import { isStorageSync } from '../trackers/impressionObserver/utils';
9+
import { isConsumerMode } from '../utils/settingsValidation/mode';
1010
import { SPLIT_FN_LABEL, SPLITS_FN_LABEL, NAMES_FN_LABEL } from '../utils/constants';
1111

1212
function collectTreatments(splitObject: ISplit) {
@@ -51,8 +51,8 @@ export function sdkManagerFactory<TSplitCache extends ISplitsCacheSync | ISplits
5151
{ readinessManager, sdkStatus }: ISdkReadinessManager,
5252
): TSplitCache extends ISplitsCacheAsync ? SplitIO.IAsyncManager : SplitIO.IManager {
5353

54-
const log = settings.log;
55-
const isSync = isStorageSync(settings);
54+
const { log, mode } = settings;
55+
const isAsync = isConsumerMode(mode);
5656

5757
return objectAssign(
5858
// Proto-linkage of the readiness Event Emitter
@@ -64,7 +64,7 @@ export function sdkManagerFactory<TSplitCache extends ISplitsCacheSync | ISplits
6464
split(featureFlagName: string) {
6565
const splitName = validateSplit(log, featureFlagName, SPLIT_FN_LABEL);
6666
if (!validateIfNotDestroyed(log, readinessManager, SPLIT_FN_LABEL) || !validateIfOperational(log, readinessManager, SPLIT_FN_LABEL) || !splitName) {
67-
return isSync ? null : Promise.resolve(null);
67+
return isAsync ? Promise.resolve(null) : null;
6868
}
6969

7070
const split = splits.getSplit(splitName);
@@ -85,7 +85,7 @@ export function sdkManagerFactory<TSplitCache extends ISplitsCacheSync | ISplits
8585
*/
8686
splits() {
8787
if (!validateIfNotDestroyed(log, readinessManager, SPLITS_FN_LABEL) || !validateIfOperational(log, readinessManager, SPLITS_FN_LABEL)) {
88-
return isSync ? [] : Promise.resolve([]);
88+
return isAsync ? Promise.resolve([]) : [];
8989
}
9090
const currentSplits = splits.getAll();
9191

@@ -98,7 +98,7 @@ export function sdkManagerFactory<TSplitCache extends ISplitsCacheSync | ISplits
9898
*/
9999
names() {
100100
if (!validateIfNotDestroyed(log, readinessManager, NAMES_FN_LABEL) || !validateIfOperational(log, readinessManager, NAMES_FN_LABEL)) {
101-
return isSync ? [] : Promise.resolve([]);
101+
return isAsync ? Promise.resolve([]) : [];
102102
}
103103
const splitNames = splits.getSplitNames();
104104

src/storages/KeyBuilder.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import { ISettings } from '../types';
12
import { startsWith } from '../utils/lang';
3+
import { hash } from '../utils/murmur3/murmur3';
24

35
const everythingAtTheEnd = /[^.]+$/;
46

@@ -10,7 +12,7 @@ export function validatePrefix(prefix: unknown) {
1012

1113
export class KeyBuilder {
1214

13-
protected readonly prefix: string;
15+
readonly prefix: string;
1416

1517
constructor(prefix: string = DEFAULT_PREFIX) {
1618
this.prefix = prefix;
@@ -73,4 +75,15 @@ export class KeyBuilder {
7375
}
7476
}
7577

78+
buildHashKey() {
79+
return `${this.prefix}.hash`;
80+
}
81+
}
82+
83+
/**
84+
* Generates a murmur32 hash based on the authorization key and the feature flags filter query.
85+
* The hash is in hexadecimal format (8 characters max, 32 bits).
86+
*/
87+
export function getStorageHash(settings: ISettings) {
88+
return hash(`${settings.core.authorizationKey}::${settings.sync.__splitFiltersValidation.queryString}`).toString(16);
7689
}

src/storages/KeyBuilderCS.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export class KeyBuilderCS extends KeyBuilder {
99
constructor(prefix: string, matchingKey: string) {
1010
super(prefix);
1111
this.matchingKey = matchingKey;
12-
this.regexSplitsCacheKey = new RegExp(`^${prefix}\\.(splits?|trafficType)\\.`);
12+
this.regexSplitsCacheKey = new RegExp(`^${prefix}\\.(splits?|trafficType|flagSet)\\.`);
1313
}
1414

1515
/**
@@ -45,8 +45,4 @@ export class KeyBuilderCS extends KeyBuilder {
4545
isSplitsCacheKey(key: string) {
4646
return this.regexSplitsCacheKey.test(key);
4747
}
48-
49-
buildSplitsFilterQueryKey() {
50-
return `${this.prefix}.splits.filterQuery`;
51-
}
5248
}

src/storages/KeyBuilderSS.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ export const METHOD_NAMES: Record<Method, string> = {
1616

1717
export class KeyBuilderSS extends KeyBuilder {
1818

19-
latencyPrefix: string;
20-
exceptionPrefix: string;
21-
initPrefix: string;
22-
private versionablePrefix: string;
19+
readonly latencyPrefix: string;
20+
readonly exceptionPrefix: string;
21+
readonly initPrefix: string;
22+
private readonly versionablePrefix: string;
2323

2424
constructor(prefix: string, metadata: IMetadata) {
2525
super(prefix);

src/storages/__tests__/KeyBuilder.spec.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { KeyBuilder } from '../KeyBuilder';
1+
import { ISettings } from '../../types';
2+
import { KeyBuilder, getStorageHash } from '../KeyBuilder';
23
import { KeyBuilderSS } from '../KeyBuilderSS';
34

45
test('KEYS / splits keys', () => {
@@ -111,3 +112,20 @@ test('KEYS / latency and exception keys (telemetry)', () => {
111112
const expectedExceptionKey = `${prefix}.telemetry.exceptions::${metadata.s}/${metadata.n}/${metadata.i}/treatment`;
112113
expect(builder.buildExceptionKey(methodName)).toBe(expectedExceptionKey);
113114
});
115+
116+
test('getStorageHash', () => {
117+
expect(getStorageHash({
118+
core: { authorizationKey: '<fake-token-rfc>' },
119+
sync: { __splitFiltersValidation: { queryString: '&names=p1__split,p2__split' } }
120+
} as ISettings)).toBe('6a596ded');
121+
122+
expect(getStorageHash({
123+
core: { authorizationKey: '<fake-token-rfc>' },
124+
sync: { __splitFiltersValidation: { queryString: '&names=p2__split,p3__split' } }
125+
} as ISettings)).toBe('18ad36f3');
126+
127+
expect(getStorageHash({
128+
core: { authorizationKey: '<fake-token-rfc>' },
129+
sync: { __splitFiltersValidation: { queryString: null } }
130+
} as ISettings)).toBe('9507ef4');
131+
});

0 commit comments

Comments
 (0)