1+ import { ISettings } from '../../../types' ;
2+ import { ISplitChangesResponse } from '../../../dtos/types' ;
13import { 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' ;
27import { 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+ }
316
417/**
518 * Factory of SplitChanges fetcher.
619 * SplitChanges fetcher is a wrapper around `splitChanges` API service that parses the response and handle errors.
720 */
8- export function splitChangesFetcherFactory ( fetchSplitChanges : IFetchSplitChanges ) : ISplitChangesFetcher {
21+ // @TODO breaking: drop support for Split Proxy below v5.10.0 and simplify the implementation
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 ;
927
1028 return function splitChangesFetcher (
1129 since : number ,
@@ -14,12 +32,51 @@ export function splitChangesFetcherFactory(fetchSplitChanges: IFetchSplitChanges
1432 rbSince ?: number ,
1533 // Optional decorator for `fetchSplitChanges` promise, such as timeout or time tracker
1634 decorator ?: ( promise : Promise < IResponse > ) => Promise < IResponse >
17- ) {
35+ ) : Promise < ISplitChangesResponse > {
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+ } ) ;
1853
19- let splitsPromise = fetchSplitChanges ( since , noCache , till , rbSince ) ;
2054 if ( decorator ) splitsPromise = decorator ( splitsPromise ) ;
2155
22- return splitsPromise . then ( resp => resp . json ( ) ) ;
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+ } ) ;
2380 } ;
2481
2582}
0 commit comments