1
+ import { ISettings } from '../../../types' ;
2
+ import { ISplitChangesResponse } from '../../../dtos/types' ;
1
3
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' ;
2
7
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
+ }
3
16
4
17
/**
5
18
* Factory of SplitChanges fetcher.
6
19
* SplitChanges fetcher is a wrapper around `splitChanges` API service that parses the response and handle errors.
7
20
*/
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 ;
9
27
10
28
return function splitChangesFetcher (
11
29
since : number ,
@@ -14,12 +32,51 @@ export function splitChangesFetcherFactory(fetchSplitChanges: IFetchSplitChanges
14
32
rbSince ?: number ,
15
33
// Optional decorator for `fetchSplitChanges` promise, such as timeout or time tracker
16
34
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
+ } ) ;
18
53
19
- let splitsPromise = fetchSplitChanges ( since , noCache , till , rbSince ) ;
20
54
if ( decorator ) splitsPromise = decorator ( splitsPromise ) ;
21
55
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
+ } ) ;
23
80
} ;
24
81
25
82
}
0 commit comments