Skip to content

Commit 6bb988c

Browse files
Update splitChangesFetcher to handle proxy error with spec v1.3
1 parent 5b056eb commit 6bb988c

File tree

4 files changed

+63
-14
lines changed

4 files changed

+63
-14
lines changed

src/services/splitApi.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ export function splitApiFactory(
2929
const urls = settings.urls;
3030
const filterQueryString = settings.sync.__splitFiltersValidation && settings.sync.__splitFiltersValidation.queryString;
3131
const SplitSDKImpressionsMode = settings.sync.impressionsMode;
32-
const flagSpecVersion = settings.sync.flagSpecVersion;
3332
const splitHttpClient = splitHttpClientFactory(settings, platform);
3433

3534
return {
@@ -45,7 +44,7 @@ export function splitApiFactory(
4544
},
4645

4746
fetchAuth(userMatchingKeys?: string[]) {
48-
let url = `${urls.auth}/v2/auth?s=${flagSpecVersion}`;
47+
let url = `${urls.auth}/v2/auth?s=${settings.sync.flagSpecVersion}`;
4948
if (userMatchingKeys) { // `userMatchingKeys` is undefined in server-side
5049
const queryParams = userMatchingKeys.map(userKeyToQueryParam).join('&');
5150
if (queryParams) url += '&' + queryParams;
@@ -54,7 +53,7 @@ export function splitApiFactory(
5453
},
5554

5655
fetchSplitChanges(since: number, noCache?: boolean, till?: number, rbSince?: number) {
57-
const url = `${urls.sdk}/splitChanges?s=${flagSpecVersion}&since=${since}${rbSince ? '&rbSince=' + rbSince : ''}${filterQueryString || ''}${till ? '&till=' + till : ''}`;
56+
const url = `${urls.sdk}/splitChanges?s=${settings.sync.flagSpecVersion}&since=${since}${rbSince ? '&rbSince=' + rbSince : ''}${filterQueryString || ''}${till ? '&till=' + till : ''}`;
5857
return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SPLITS))
5958
.catch((err) => {
6059
if (err.statusCode === 414) settings.log.error(ERROR_TOO_MANY_SETS);
Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,26 @@
1+
import { ISettings } from '../../../../types/splitio';
2+
import { ISplitChangesResponse } from '../../../dtos/types';
13
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';
27
import { ISplitChangesFetcher } from './types';
38

9+
const PROXY_CHECK_INTERVAL_MILLIS_CS = 60 * 60 * 1000; // 1 hour in Client Side
10+
const PROXY_CHECK_INTERVAL_MILLIS_SS = 24 * PROXY_CHECK_INTERVAL_MILLIS_CS; // 24 hours in Server Side
11+
12+
function sdkEndpointOverriden(settings: ISettings) {
13+
return settings.urls.sdk !== base.urls.sdk;
14+
}
15+
416
/**
517
* Factory of SplitChanges fetcher.
618
* SplitChanges fetcher is a wrapper around `splitChanges` API service that parses the response and handle errors.
719
*/
8-
export function splitChangesFetcherFactory(fetchSplitChanges: IFetchSplitChanges): ISplitChangesFetcher {
20+
export function splitChangesFetcherFactory(fetchSplitChanges: IFetchSplitChanges, settings: ISettings, storage: Pick<IStorageBase, 'splits' | 'rbSegments'>): ISplitChangesFetcher {
21+
22+
const PROXY_CHECK_INTERVAL_MILLIS = settings.core.key !== undefined ? PROXY_CHECK_INTERVAL_MILLIS_CS : PROXY_CHECK_INTERVAL_MILLIS_SS;
23+
let _lastProxyCheckTimestamp: number | undefined;
924

1025
return function splitChangesFetcher(
1126
since: number,
@@ -14,12 +29,48 @@ export function splitChangesFetcherFactory(fetchSplitChanges: IFetchSplitChanges
1429
rbSince?: number,
1530
// Optional decorator for `fetchSplitChanges` promise, such as timeout or time tracker
1631
decorator?: (promise: Promise<IResponse>) => Promise<IResponse>
17-
) {
32+
): Promise<ISplitChangesResponse> {
33+
34+
if (_lastProxyCheckTimestamp && (Date.now() - _lastProxyCheckTimestamp) > PROXY_CHECK_INTERVAL_MILLIS) {
35+
settings.sync.flagSpecVersion = FLAG_SPEC_VERSION;
36+
}
37+
38+
let splitsPromise = fetchSplitChanges(since, noCache, till, rbSince)
39+
// Handle proxy errors with spec 1.3
40+
.catch((err) => {
41+
if (err.statusCode === 400 && sdkEndpointOverriden(settings) && settings.sync.flagSpecVersion === FLAG_SPEC_VERSION) {
42+
_lastProxyCheckTimestamp = Date.now();
43+
settings.sync.flagSpecVersion = '1.2'; // fallback to 1.2 spec
44+
return fetchSplitChanges(since, noCache, till); // retry request without rbSince
45+
}
46+
throw err;
47+
});
1848

19-
let splitsPromise = fetchSplitChanges(since, noCache, till, rbSince);
2049
if (decorator) splitsPromise = decorator(splitsPromise);
2150

22-
return splitsPromise.then(resp => resp.json());
51+
return splitsPromise
52+
.then(resp => resp.json())
53+
.then(data => {
54+
// Using flag spec version 1.2
55+
if (data.splits) {
56+
return {
57+
ff: {
58+
d: data.splits,
59+
s: data.since,
60+
t: data.till
61+
}
62+
};
63+
}
64+
65+
// Proxy recovery
66+
if (_lastProxyCheckTimestamp) {
67+
_lastProxyCheckTimestamp = undefined;
68+
return Promise.all([storage.splits.clear(), storage.rbSegments.clear()])
69+
.then(() => splitChangesFetcher(-1, undefined, undefined, -1));
70+
}
71+
72+
return data;
73+
});
2374
};
2475

2576
}

src/sync/polling/syncTasks/splitsSyncTask.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export function splitsSyncTaskFactory(
2121
settings.log,
2222
splitChangesUpdaterFactory(
2323
settings.log,
24-
splitChangesFetcherFactory(fetchSplitChanges),
24+
splitChangesFetcherFactory(fetchSplitChanges, settings, storage),
2525
storage,
2626
settings.sync.__splitFiltersValidation,
2727
readiness.splits,

src/sync/polling/updaters/__tests__/splitChangesUpdater.spec.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -156,12 +156,6 @@ test('splitChangesUpdater / compute splits mutation with filters', () => {
156156
});
157157

158158
describe('splitChangesUpdater', () => {
159-
160-
fetchMock.once('*', { status: 200, body: splitChangesMock1 }); // @ts-ignore
161-
const splitApi = splitApiFactory(settingsSplitApi, { getFetch: () => fetchMock }, telemetryTrackerFactory());
162-
const fetchSplitChanges = jest.spyOn(splitApi, 'fetchSplitChanges');
163-
const splitChangesFetcher = splitChangesFetcherFactory(splitApi.fetchSplitChanges);
164-
165159
const splits = new SplitsCacheInMemory();
166160
const updateSplits = jest.spyOn(splits, 'update');
167161

@@ -173,6 +167,11 @@ describe('splitChangesUpdater', () => {
173167

174168
const storage = { splits, rbSegments, segments };
175169

170+
fetchMock.once('*', { status: 200, body: splitChangesMock1 }); // @ts-ignore
171+
const splitApi = splitApiFactory(settingsSplitApi, { getFetch: () => fetchMock }, telemetryTrackerFactory());
172+
const fetchSplitChanges = jest.spyOn(splitApi, 'fetchSplitChanges');
173+
const splitChangesFetcher = splitChangesFetcherFactory(splitApi.fetchSplitChanges, fullSettings, storage);
174+
176175
const readinessManager = readinessManagerFactory(EventEmitter, fullSettings);
177176
const splitsEmitSpy = jest.spyOn(readinessManager.splits, 'emit');
178177

0 commit comments

Comments
 (0)