Skip to content

Commit d7c5285

Browse files
Merge branch 'development' of github.com:splitio/java-client into revalidate-stale-connections
2 parents 1ad23f2 + 99cf461 commit d7c5285

29 files changed

+1124
-111
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ Split has built and maintains SDKs for:
6464
* Java [Github](https://github.com/splitio/java-client) [Docs](https://help.split.io/hc/en-us/articles/360020405151-Java-SDK)
6565
* Javascript [Github](https://github.com/splitio/javascript-client) [Docs](https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK)
6666
* Node [Github](https://github.com/splitio/javascript-client) [Docs](https://help.split.io/hc/en-us/articles/360020564931-Node-js-SDK)
67-
* .NET [Github](https://github.com/splitio/.net-core-client) [Docs](https://help.split.io/hc/en-us/articles/360020240172--NET-SDK)
67+
* .NET [Github](https://github.com/splitio/dotnet-client) [Docs](https://help.split.io/hc/en-us/articles/360020240172--NET-SDK)
6868
* Ruby [Github](https://github.com/splitio/ruby-client) [Docs](https://help.split.io/hc/en-us/articles/360020673251-Ruby-SDK)
6969
* PHP [Github](https://github.com/splitio/php-client) [Docs](https://help.split.io/hc/en-us/articles/360020350372-PHP-SDK)
7070
* Python [Github](https://github.com/splitio/python-client) [Docs](https://help.split.io/hc/en-us/articles/360020359652-Python-SDK)

client/src/main/java/io/split/client/HttpSegmentChangeFetcher.java

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
import io.split.client.dtos.SegmentChange;
55
import io.split.client.utils.Json;
66
import io.split.client.utils.Utils;
7+
import io.split.engine.common.FetchOptions;
78
import io.split.engine.metrics.Metrics;
89
import io.split.engine.segments.SegmentChangeFetcher;
910
import org.apache.hc.client5.http.classic.methods.HttpGet;
1011
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
1112
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
13+
import org.apache.hc.core5.http.Header;
1214
import org.apache.hc.core5.http.io.entity.EntityUtils;
1315
import org.apache.hc.core5.net.URIBuilder;
1416
import org.slf4j.Logger;
@@ -17,6 +19,8 @@
1719
import java.net.URI;
1820
import java.net.URISyntaxException;
1921
import java.nio.charset.StandardCharsets;
22+
import java.util.Arrays;
23+
import java.util.stream.Collectors;
2024

2125
import static com.google.common.base.Preconditions.checkNotNull;
2226

@@ -27,9 +31,13 @@ public final class HttpSegmentChangeFetcher implements SegmentChangeFetcher {
2731
private static final Logger _log = LoggerFactory.getLogger(HttpSegmentChangeFetcher.class);
2832

2933
private static final String SINCE = "since";
34+
private static final String TILL = "till";
3035
private static final String PREFIX = "segmentChangeFetcher";
31-
private static final String NAME_CACHE = "Cache-Control";
32-
private static final String VALUE_CACHE = "no-cache";
36+
private static final String CACHE_CONTROL_HEADER_NAME = "Cache-Control";
37+
private static final String CACHE_CONTROL_HEADER_VALUE = "no-cache";
38+
39+
private static final String HEADER_FASTLY_DEBUG_NAME = "Fastly-Debug";
40+
private static final String HEADER_FASTLY_DEBUG_VALUE = "1";
3341

3442
private final CloseableHttpClient _client;
3543
private final URI _target;
@@ -51,19 +59,34 @@ private HttpSegmentChangeFetcher(CloseableHttpClient client, URI uri, Metrics me
5159
}
5260

5361
@Override
54-
public SegmentChange fetch(String segmentName, long since, boolean addCacheHeader) {
62+
public SegmentChange fetch(String segmentName, long since, FetchOptions options) {
5563
long start = System.currentTimeMillis();
5664

5765
CloseableHttpResponse response = null;
5866

5967
try {
6068
String path = _target.getPath() + "/" + segmentName;
61-
URI uri = new URIBuilder(_target).setPath(path).addParameter(SINCE, "" + since).build();
69+
URIBuilder uriBuilder = new URIBuilder(_target)
70+
.setPath(path)
71+
.addParameter(SINCE, "" + since);
72+
if (options.hasCustomCN()) {
73+
uriBuilder.addParameter(TILL, "" + options.targetCN());
74+
}
75+
76+
URI uri = uriBuilder.build();
6277
HttpGet request = new HttpGet(uri);
63-
if(addCacheHeader) {
64-
request.setHeader(NAME_CACHE, VALUE_CACHE);
78+
79+
if(options.cacheControlHeadersEnabled()) {
80+
request.setHeader(CACHE_CONTROL_HEADER_NAME, CACHE_CONTROL_HEADER_VALUE);
6581
}
82+
83+
if (options.fastlyDebugHeaderEnabled()) {
84+
request.addHeader(HEADER_FASTLY_DEBUG_NAME, HEADER_FASTLY_DEBUG_VALUE);
85+
}
86+
6687
response = _client.execute(request);
88+
options.handleResponseHeaders(Arrays.stream(response.getHeaders())
89+
.collect(Collectors.toMap(Header::getName, Header::getValue)));
6790

6891
int statusCode = response.getCode();
6992

client/src/main/java/io/split/client/HttpSplitChangeFetcher.java

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
import io.split.client.dtos.SplitChange;
55
import io.split.client.utils.Json;
66
import io.split.client.utils.Utils;
7+
import io.split.engine.common.FetchOptions;
78
import io.split.engine.experiments.SplitChangeFetcher;
89
import io.split.engine.metrics.Metrics;
910
import org.apache.hc.client5.http.classic.methods.HttpGet;
1011
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
1112
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
13+
import org.apache.hc.core5.http.Header;
1214
import org.apache.hc.core5.http.io.entity.EntityUtils;
1315
import org.apache.hc.core5.net.URIBuilder;
1416
import org.slf4j.Logger;
@@ -17,6 +19,8 @@
1719
import java.net.URI;
1820
import java.net.URISyntaxException;
1921
import java.nio.charset.StandardCharsets;
22+
import java.util.Arrays;
23+
import java.util.stream.Collectors;
2024

2125
import static com.google.common.base.Preconditions.checkNotNull;
2226

@@ -27,9 +31,14 @@ public final class HttpSplitChangeFetcher implements SplitChangeFetcher {
2731
private static final Logger _log = LoggerFactory.getLogger(HttpSplitChangeFetcher.class);
2832

2933
private static final String SINCE = "since";
34+
private static final String TILL = "till";
3035
private static final String PREFIX = "splitChangeFetcher";
31-
private static final String NAME_CACHE = "Cache-Control";
32-
private static final String VALUE_CACHE = "no-cache";
36+
37+
private static final String HEADER_CACHE_CONTROL_NAME = "Cache-Control";
38+
private static final String HEADER_CACHE_CONTROL_VALUE = "no-cache";
39+
40+
private static final String HEADER_FASTLY_DEBUG_NAME = "Fastly-Debug";
41+
private static final String HEADER_FASTLY_DEBUG_VALUE = "1";
3342

3443
private final CloseableHttpClient _client;
3544
private final URI _target;
@@ -50,21 +59,37 @@ private HttpSplitChangeFetcher(CloseableHttpClient client, URI uri, Metrics metr
5059
checkNotNull(_target);
5160
}
5261

62+
long makeRandomTill() {
63+
64+
return (-1)*(long)Math.floor(Math.random()*(Math.pow(2, 63)));
65+
}
66+
5367
@Override
54-
public SplitChange fetch(long since, boolean addCacheHeader) {
68+
public SplitChange fetch(long since, FetchOptions options) {
5569

5670
long start = System.currentTimeMillis();
5771

5872
CloseableHttpResponse response = null;
5973

6074
try {
61-
URI uri = new URIBuilder(_target).addParameter(SINCE, "" + since).build();
75+
URIBuilder uriBuilder = new URIBuilder(_target).addParameter(SINCE, "" + since);
76+
if (options.hasCustomCN()) {
77+
uriBuilder.addParameter(TILL, "" + options.targetCN());
78+
}
79+
URI uri = uriBuilder.build();
6280

6381
HttpGet request = new HttpGet(uri);
64-
if(addCacheHeader) {
65-
request.setHeader(NAME_CACHE, VALUE_CACHE);
82+
if(options.cacheControlHeadersEnabled()) {
83+
request.setHeader(HEADER_CACHE_CONTROL_NAME, HEADER_CACHE_CONTROL_VALUE);
84+
}
85+
86+
if (options.fastlyDebugHeaderEnabled()) {
87+
request.addHeader(HEADER_FASTLY_DEBUG_NAME, HEADER_FASTLY_DEBUG_VALUE);
6688
}
89+
6790
response = _client.execute(request);
91+
options.handleResponseHeaders(Arrays.stream(response.getHeaders())
92+
.collect(Collectors.toMap(Header::getName, Header::getValue)));
6893

6994
int statusCode = response.getCode();
7095

client/src/main/java/io/split/client/SplitClientConfig.java

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ public class SplitClientConfig {
4747
private final String _authServiceURL;
4848
private final String _streamingServiceURL;
4949
private long _validateAfterInactivityInMillis;
50+
private final int _onDemandFetchRetryDelayMs;
51+
private final int _onDemandFetchMaxRetries;
52+
private final int _failedAttemptsBeforeLogging;
53+
private final boolean _cdnDebugLogging;
5054

5155
// Proxy configs
5256
private final HttpHost _proxy;
@@ -91,7 +95,11 @@ private SplitClientConfig(String endpoint,
9195
int streamingReconnectBackoffBase,
9296
String authServiceURL,
9397
String streamingServiceURL,
94-
long validateAfterInactivityInMillis) {
98+
long validateAfterInactivityInMillis,
99+
int onDemandFetchRetryDelayMs,
100+
int onDemandFetchMaxRetries,
101+
int failedAttemptsBeforeLogging,
102+
boolean cdnDebugLogging) {
95103
_endpoint = endpoint;
96104
_eventsEndpoint = eventsEndpoint;
97105
_featuresRefreshRate = pollForFeatureChangesEveryNSeconds;
@@ -123,6 +131,10 @@ private SplitClientConfig(String endpoint,
123131
_authServiceURL = authServiceURL;
124132
_streamingServiceURL = streamingServiceURL;
125133
_validateAfterInactivityInMillis = validateAfterInactivityInMillis;
134+
_onDemandFetchRetryDelayMs = onDemandFetchRetryDelayMs;
135+
_onDemandFetchMaxRetries = onDemandFetchMaxRetries;
136+
_failedAttemptsBeforeLogging = failedAttemptsBeforeLogging;
137+
_cdnDebugLogging = cdnDebugLogging;
126138

127139
Properties props = new Properties();
128140
try {
@@ -254,6 +266,14 @@ public String streamingServiceURL() {
254266
public long validateAfterInactivityInMillis() {
255267
return _validateAfterInactivityInMillis;
256268
}
269+
public int streamingRetryDelay() {return _onDemandFetchRetryDelayMs;}
270+
271+
public int streamingFetchMaxRetries() {return _onDemandFetchMaxRetries;}
272+
273+
public int failedAttemptsBeforeLogging() {return _failedAttemptsBeforeLogging;}
274+
275+
public boolean cdnDebugLogging() { return _cdnDebugLogging; }
276+
257277

258278
public static final class Builder {
259279

@@ -291,6 +311,10 @@ public static final class Builder {
291311
private String _authServiceURL = "https://auth.split.io/api/auth";
292312
private String _streamingServiceURL = "https://streaming.split.io/sse";
293313
private long _validateAfterInactivityInMillis = -1;
314+
private final int _onDemandFetchRetryDelayMs = 50;
315+
private final int _onDemandFetchMaxRetries = 10;
316+
private final int _failedAttemptsBeforeLogging = 10;
317+
private final boolean _cdnDebugLogging = true;
294318

295319
public Builder() {
296320
}
@@ -762,6 +786,13 @@ public SplitClientConfig build() {
762786
throw new IllegalArgumentException("streamingServiceURL must not be null");
763787
}
764788

789+
if(_onDemandFetchRetryDelayMs <= 0) {
790+
throw new IllegalStateException("streamingRetryDelay must be > 0");
791+
}
792+
if(_onDemandFetchMaxRetries <= 0) {
793+
throw new IllegalStateException("_onDemandFetchMaxRetries must be > 0");
794+
}
795+
765796
return new SplitClientConfig(
766797
_endpoint,
767798
_eventsEndpoint,
@@ -793,7 +824,11 @@ public SplitClientConfig build() {
793824
_streamingReconnectBackoffBase,
794825
_authServiceURL,
795826
_streamingServiceURL,
796-
_validateAfterInactivityInMillis);
827+
_validateAfterInactivityInMillis,
828+
_onDemandFetchRetryDelayMs,
829+
_onDemandFetchMaxRetries,
830+
_failedAttemptsBeforeLogging,
831+
_cdnDebugLogging);
797832
}
798833
}
799834
}

client/src/main/java/io/split/client/SplitFactoryImpl.java

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,9 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
130130
_splitFetcher = buildSplitFetcher();
131131

132132
// SplitSynchronizationTask
133-
_splitSynchronizationTask = new SplitSynchronizationTask(_splitFetcher, _splitCache, findPollingPeriod(RANDOM, config.featuresRefreshRate()));
133+
_splitSynchronizationTask = new SplitSynchronizationTask(_splitFetcher,
134+
_splitCache,
135+
findPollingPeriod(RANDOM, config.featuresRefreshRate()));
134136

135137
// Impressions
136138
_impressionsManager = buildImpressionsManager(config);
@@ -139,27 +141,54 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
139141
_cachedFireAndForgetMetrics = buildCachedFireAndForgetMetrics(config);
140142

141143
// EventClient
142-
_eventClient = EventClientImpl.create(_httpclient, _eventsRootTarget, config.eventsQueueSize(), config.eventFlushIntervalInMillis(), config.waitBeforeShutdown());
144+
_eventClient = EventClientImpl.create(_httpclient,
145+
_eventsRootTarget,
146+
config.eventsQueueSize(),
147+
config.eventFlushIntervalInMillis(),
148+
config.waitBeforeShutdown());
143149

144150
// SyncManager
145-
_syncManager = SyncManagerImp.build(config.streamingEnabled(), _splitSynchronizationTask, _splitFetcher, _segmentSynchronizationTaskImp, _splitCache, config.authServiceURL(), _httpclient, config.streamingServiceURL(), config.authRetryBackoffBase(), buildSSEdHttpClient(config), _segmentCache);
151+
_syncManager = SyncManagerImp.build(config.streamingEnabled(),
152+
_splitSynchronizationTask,
153+
_splitFetcher,
154+
_segmentSynchronizationTaskImp,
155+
_splitCache,
156+
config.authServiceURL(),
157+
_httpclient,
158+
config.streamingServiceURL(),
159+
config.authRetryBackoffBase(),
160+
buildSSEdHttpClient(config),
161+
_segmentCache,
162+
config.streamingRetryDelay(),
163+
config.streamingFetchMaxRetries(),
164+
config.failedAttemptsBeforeLogging(),
165+
config.cdnDebugLogging());
146166
_syncManager.start();
147167

148168
// Evaluator
149169
_evaluator = new EvaluatorImp(_splitCache);
150170

151171
// SplitClient
152-
_client = new SplitClientImpl(this, _splitCache, _impressionsManager, _cachedFireAndForgetMetrics, _eventClient, config, _gates, _evaluator);
172+
_client = new SplitClientImpl(this,
173+
_splitCache,
174+
_impressionsManager,
175+
_cachedFireAndForgetMetrics,
176+
_eventClient,
177+
config,
178+
_gates,
179+
_evaluator);
153180

154181
// SplitManager
155182
_manager = new SplitManagerImpl(_splitCache, config, _gates);
156183

157184
// DestroyOnShutDown
158185
if (config.destroyOnShutDown()) {
159-
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
186+
Thread shutdown = new Thread(() -> {
160187
// Using the full path to avoid conflicting with Thread.destroy()
161188
SplitFactoryImpl.this.destroy();
162-
}));
189+
});
190+
shutdown.setName("split-destroy-worker");
191+
Runtime.getRuntime().addShutdownHook(shutdown);
163192
}
164193
}
165194

@@ -208,7 +237,6 @@ public boolean isDestroyed() {
208237
}
209238

210239
private static CloseableHttpClient buildHttpClient(String apiToken, SplitClientConfig config) {
211-
212240
SSLConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactoryBuilder.create()
213241
.setSslContext(SSLContexts.createSystemDefault())
214242
.setTlsVersions(TLS.V_1_2)

client/src/main/java/io/split/client/jmx/SplitJmxMonitor.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import io.split.cache.SegmentCache;
44
import io.split.cache.SplitCache;
55
import io.split.client.SplitClient;
6+
import io.split.engine.common.FetchOptions;
67
import io.split.engine.experiments.SplitFetcher;
78
import io.split.engine.segments.SegmentFetcher;
89
import io.split.engine.segments.SegmentSynchronizationTask;
@@ -34,7 +35,7 @@ public SplitJmxMonitor(SplitClient splitClient, SplitFetcher featureFetcher, Spl
3435

3536
@Override
3637
public boolean forceSyncFeatures() {
37-
_featureFetcher.forceRefresh(true);
38+
_featureFetcher.forceRefresh(new FetchOptions.Builder().cacheControlHeaders(true).build());
3839
_log.info("Features successfully refreshed via JMX");
3940
return true;
4041
}
@@ -43,7 +44,7 @@ public boolean forceSyncFeatures() {
4344
public boolean forceSyncSegment(String segmentName) {
4445
SegmentFetcher fetcher = _segmentSynchronizationTask.getFetcher(segmentName);
4546
try{
46-
fetcher.fetch(true);
47+
fetcher.fetch(new FetchOptions.Builder().build());
4748
}
4849
//We are sure this will never happen because getFetcher firts initiate the segment. This try/catch is for safe only.
4950
catch (NullPointerException np){

client/src/main/java/io/split/engine/common/Backoff.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,26 @@
55
import static com.google.common.base.Preconditions.checkNotNull;
66

77
public class Backoff {
8-
private static final long BACKOFF_MAX_SECONDS_ALLOWED = 1800;
8+
private static final long BACKOFF_MAX_ALLOWED = 1800;
99

1010
private final long _backoffBase;
1111
private AtomicInteger _attempt;
12+
private final long _maxAllowed;
1213

1314
public Backoff(long backoffBase) {
15+
this(backoffBase, BACKOFF_MAX_ALLOWED);
16+
}
17+
18+
public Backoff(long backoffBase, long maxAllowed) {
1419
_backoffBase = checkNotNull(backoffBase);
1520
_attempt = new AtomicInteger(0);
21+
_maxAllowed = maxAllowed;
1622
}
1723

1824
public long interval() {
1925
long interval = _backoffBase * (long) Math.pow(2, _attempt.getAndIncrement());
2026

21-
return interval >= BACKOFF_MAX_SECONDS_ALLOWED ? BACKOFF_MAX_SECONDS_ALLOWED : interval;
27+
return interval >= _maxAllowed ? BACKOFF_MAX_ALLOWED : interval;
2228
}
2329

2430
public synchronized void reset() {

0 commit comments

Comments
 (0)