Skip to content

Commit 71c0ca8

Browse files
committed
add logging for cdn headers
1 parent 8788b77 commit 71c0ca8

File tree

15 files changed

+200
-37
lines changed

15 files changed

+200
-37
lines changed

client/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<parent>
66
<groupId>io.split.client</groupId>
77
<artifactId>java-client-parent</artifactId>
8-
<version>4.1.7-rc1</version>
8+
<version>4.1.7-rc2</version>
99
</parent>
1010
<artifactId>java-client</artifactId>
1111
<packaging>jar</packaging>

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,9 @@ public String streamingServiceURL() {
256256

257257
public int streamingRetryDelay() {return _onDemandFetchRetryDelayMs;}
258258

259+
public boolean cdnDebugLogging() { return _cdnDebugLogging; }
260+
261+
259262
public static final class Builder {
260263

261264
private String _endpoint = "https://sdk.split.io";

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

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

131131
// SplitSynchronizationTask
132-
_splitSynchronizationTask = new SplitSynchronizationTask(_splitFetcher, _splitCache, findPollingPeriod(RANDOM, config.featuresRefreshRate()));
132+
_splitSynchronizationTask = new SplitSynchronizationTask(_splitFetcher,
133+
_splitCache,
134+
findPollingPeriod(RANDOM, config.featuresRefreshRate()));
133135

134136
// Impressions
135137
_impressionsManager = buildImpressionsManager(config);
@@ -138,27 +140,52 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
138140
_cachedFireAndForgetMetrics = buildCachedFireAndForgetMetrics(config);
139141

140142
// EventClient
141-
_eventClient = EventClientImpl.create(_httpclient, _eventsRootTarget, config.eventsQueueSize(), config.eventFlushIntervalInMillis(), config.waitBeforeShutdown());
143+
_eventClient = EventClientImpl.create(_httpclient,
144+
_eventsRootTarget,
145+
config.eventsQueueSize(),
146+
config.eventFlushIntervalInMillis(),
147+
config.waitBeforeShutdown());
142148

143149
// SyncManager
144-
_syncManager = SyncManagerImp.build(config.streamingEnabled(), _splitSynchronizationTask, _splitFetcher, _segmentSynchronizationTaskImp, _splitCache, config.authServiceURL(), _httpclient, config.streamingServiceURL(), config.authRetryBackoffBase(), buildSSEdHttpClient(config), _segmentCache, config.streamingRetryDelay());
150+
_syncManager = SyncManagerImp.build(config.streamingEnabled(),
151+
_splitSynchronizationTask,
152+
_splitFetcher,
153+
_segmentSynchronizationTaskImp,
154+
_splitCache,
155+
config.authServiceURL(),
156+
_httpclient,
157+
config.streamingServiceURL(),
158+
config.authRetryBackoffBase(),
159+
buildSSEdHttpClient(config),
160+
_segmentCache,
161+
config.streamingRetryDelay(),
162+
config.cdnDebugLogging());
145163
_syncManager.start();
146164

147165
// Evaluator
148166
_evaluator = new EvaluatorImp(_splitCache);
149167

150168
// SplitClient
151-
_client = new SplitClientImpl(this, _splitCache, _impressionsManager, _cachedFireAndForgetMetrics, _eventClient, config, _gates, _evaluator);
169+
_client = new SplitClientImpl(this,
170+
_splitCache,
171+
_impressionsManager,
172+
_cachedFireAndForgetMetrics,
173+
_eventClient,
174+
config,
175+
_gates,
176+
_evaluator);
152177

153178
// SplitManager
154179
_manager = new SplitManagerImpl(_splitCache, config, _gates);
155180

156181
// DestroyOnShutDown
157182
if (config.destroyOnShutDown()) {
158-
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
183+
Thread shutdown = new Thread(() -> {
159184
// Using the full path to avoid conflicting with Thread.destroy()
160185
SplitFactoryImpl.this.destroy();
161-
}));
186+
});
187+
shutdown.setName("split-destroy-worker");
188+
Runtime.getRuntime().addShutdownHook(shutdown);
162189
}
163190
}
164191

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
public class FastlyHeadersCaptor {
77

8-
public static Set<String> FIELDS_TO_CAPTURE = new HashSet<>(Arrays.asList(
8+
private static final Set<String> HEADERS_TO_CAPTURE = new HashSet<>(Arrays.asList(
99
"Fastly-Debug-Path",
1010
"Fastly-Debug-TTL",
1111
"Fastly-Debug-Digest",
@@ -24,7 +24,7 @@ public class FastlyHeadersCaptor {
2424

2525
public Void handle(Map<String, String> responseHeaders) {
2626
_headers.add(responseHeaders.entrySet().stream()
27-
.filter(e -> FIELDS_TO_CAPTURE.contains(e.getKey()))
27+
.filter(e -> HEADERS_TO_CAPTURE.contains(e.getKey()))
2828
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
2929
return null;
3030
}

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package io.split.engine.common;
22

3+
import io.split.engine.matchers.AttributeMatcher;
4+
import org.checkerframework.checker.units.qual.A;
5+
36
import java.util.Map;
47
import java.util.Objects;
58
import java.util.function.Function;
@@ -55,6 +58,18 @@ private FetchOptions(boolean cacheControlHeaders, Function<Map<String, String>,
5558
_fastlyDebugHeader = fastlyDebugHeader;
5659
}
5760

61+
public boolean equals(Object obj) {
62+
if (null == obj) return false;
63+
if (this == obj) return true;
64+
if (!(obj instanceof FetchOptions)) return false;
65+
66+
FetchOptions other = (FetchOptions) obj;
67+
68+
return Objects.equals(_cacheControlHeaders, other._cacheControlHeaders)
69+
&& Objects.equals(_fastlyDebugHeader, other._fastlyDebugHeader)
70+
&& Objects.equals(_responseHeadersCallback, other._responseHeadersCallback);
71+
}
72+
5873
private final boolean _cacheControlHeaders;
5974
private final boolean _fastlyDebugHeader;
6075
private final Function<Map<String, String>, Void> _responseHeadersCallback;

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,10 @@ public static SyncManagerImp build(boolean streamingEnabledConfig,
6060
int authRetryBackOffBase,
6161
CloseableHttpClient sseHttpClient,
6262
SegmentCache segmentCache,
63-
int streamingRetryDelay) {
63+
int streamingRetryDelay,
64+
boolean cdnDebugLogging) {
6465
LinkedBlockingQueue<PushManager.Status> pushMessages = new LinkedBlockingQueue<>();
65-
Synchronizer synchronizer = new SynchronizerImp(splitSynchronizationTask, splitFetcher, segmentSynchronizationTaskImp, splitCache, segmentCache, streamingRetryDelay);
66+
Synchronizer synchronizer = new SynchronizerImp(splitSynchronizationTask, splitFetcher, segmentSynchronizationTaskImp, splitCache, segmentCache, streamingRetryDelay, cdnDebugLogging);
6667
PushManager pushManager = PushManagerImp.build(synchronizer, streamingServiceUrl, authUrl, httpClient, pushMessages, sseHttpClient);
6768
return new SyncManagerImp(streamingEnabledConfig, synchronizer, pushManager, pushMessages, authRetryBackOffBase);
6869
}
@@ -111,14 +112,16 @@ private void startPollingMode() {
111112
_pushManager.startWorkers();
112113
_pushManager.scheduleConnectionReset();
113114
_backoff.reset();
115+
_log.info("Streaming up and running.");
114116
break;
115117
case STREAMING_DOWN:
118+
_log.info("Streaming service temporarily unavailable, working in polling mode.");
116119
_pushManager.stopWorkers();
117120
_synchronizer.startPeriodicFetching();
118121
break;
119122
case STREAMING_BACKOFF:
120123
long howLong = _backoff.interval() * 1000;
121-
_log.error(String.format("Retryable error in streaming subsystem. Switching to polling and retrying in %d seconds", howLong/1000));
124+
_log.info(String.format("Retryable error in streaming subsystem. Switching to polling and retrying in %d seconds", howLong/1000));
122125
_synchronizer.startPeriodicFetching();
123126
_pushManager.stopWorkers();
124127
_pushManager.stop();
@@ -127,6 +130,7 @@ private void startPollingMode() {
127130
_pushManager.start();
128131
break;
129132
case STREAMING_OFF:
133+
_log.info("Unrecoverable error in streaming subsystem. SDK will work in polling-mode and will not retry an SSE connection.");
130134
_pushManager.stop();
131135
_synchronizer.startPeriodicFetching();
132136
if (null != _pushStatusMonitorTask) {

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

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@
1212
import org.slf4j.Logger;
1313
import org.slf4j.LoggerFactory;
1414

15-
import java.util.ArrayList;
16-
import java.util.List;
17-
import java.util.Map;
1815
import java.util.concurrent.Executors;
1916
import java.util.concurrent.ScheduledExecutorService;
2017
import java.util.concurrent.ThreadFactory;
@@ -23,31 +20,34 @@
2320
import static com.google.common.base.Preconditions.checkNotNull;
2421

2522
public class SynchronizerImp implements Synchronizer {
26-
private static final Logger _log = LoggerFactory.getLogger(Synchronizer.class);
27-
private static final int RETRIES_NUMBER = 10;
2823

29-
private final boolean _cdnResponseHeadersLogging = true;
24+
private static final int MAX_ATTEMPTS = 10;
25+
26+
private static final Logger _log = LoggerFactory.getLogger(Synchronizer.class);
3027
private final SplitSynchronizationTask _splitSynchronizationTask;
3128
private final SplitFetcher _splitFetcher;
3229
private final SegmentSynchronizationTask _segmentSynchronizationTaskImp;
3330
private final ScheduledExecutorService _syncAllScheduledExecutorService;
3431
private final SplitCache _splitCache;
3532
private final SegmentCache _segmentCache;
3633
private final int _onDemandFetchRetryDelayMs;
34+
private final boolean _cdnResponseHeadersLogging;
3735
private final Gson gson = new GsonBuilder().create();
3836

3937
public SynchronizerImp(SplitSynchronizationTask splitSynchronizationTask,
4038
SplitFetcher splitFetcher,
4139
SegmentSynchronizationTask segmentSynchronizationTaskImp,
4240
SplitCache splitCache,
4341
SegmentCache segmentCache,
44-
int onDemandFetchRetryDelayMs) {
42+
int onDemandFetchRetryDelayMs,
43+
boolean cdnResponseHeadersLogging) {
4544
_splitSynchronizationTask = checkNotNull(splitSynchronizationTask);
4645
_splitFetcher = checkNotNull(splitFetcher);
4746
_segmentSynchronizationTaskImp = checkNotNull(segmentSynchronizationTaskImp);
4847
_splitCache = checkNotNull(splitCache);
4948
_segmentCache = checkNotNull(segmentCache);
5049
_onDemandFetchRetryDelayMs = checkNotNull(onDemandFetchRetryDelayMs);
50+
_cdnResponseHeadersLogging = cdnResponseHeadersLogging;
5151

5252
ThreadFactory splitsThreadFactory = new ThreadFactoryBuilder()
5353
.setDaemon(true)
@@ -80,7 +80,7 @@ public void stopPeriodicFetching() {
8080

8181
@Override
8282
public void refreshSplits(long targetChangeNumber) {
83-
int retries = RETRIES_NUMBER;
83+
8484
if (targetChangeNumber <= _splitCache.getChangeNumber()) {
8585
return;
8686
}
@@ -92,18 +92,16 @@ public void refreshSplits(long targetChangeNumber) {
9292
.responseHeadersCallback(_cdnResponseHeadersLogging ? captor::handle : null)
9393
.build();
9494

95+
int remainingAttempts = MAX_ATTEMPTS;
9596
while(true) {
96-
retries--;
97+
remainingAttempts--;
9798
_splitFetcher.forceRefresh(opts);
9899
if (targetChangeNumber <= _splitCache.getChangeNumber()) {
99-
_log.debug("Refresh completed in %s attempts.", RETRIES_NUMBER - retries);
100-
return;
101-
} else if (retries <= 0) {
102-
_log.warn("No changes fetched after %s attempts.", RETRIES_NUMBER);
103-
if (_cdnResponseHeadersLogging) {
104-
_log.debug("CDN Debug headers: ", gson.toJson(captor.get()));
105-
}
106-
return;
100+
_log.debug(String.format("Refresh completed in %s attempts.", MAX_ATTEMPTS - remainingAttempts));
101+
break;
102+
} else if (remainingAttempts <= 0) {
103+
_log.info(String.format("No changes fetched after %s attempts.", MAX_ATTEMPTS));
104+
break;
107105
}
108106
try {
109107
Thread.sleep(_onDemandFetchRetryDelayMs);
@@ -112,6 +110,10 @@ public void refreshSplits(long targetChangeNumber) {
112110
_log.debug("Error trying to sleep current Thread.");
113111
}
114112
}
113+
114+
if (_cdnResponseHeadersLogging && remainingAttempts <= (MAX_ATTEMPTS / 2)) {
115+
_log.info(String.format("CDN Debug headers: %s", gson.toJson(captor.get())));
116+
}
115117
}
116118

117119
@Override
@@ -125,7 +127,7 @@ public void localKillSplit(String splitName, String defaultTreatment, long newCh
125127
@Override
126128
public void refreshSegment(String segmentName, long changeNumber) {
127129
int retries = 1;
128-
while(changeNumber > _segmentCache.getChangeNumber(segmentName) && retries <= RETRIES_NUMBER) {
130+
while(changeNumber > _segmentCache.getChangeNumber(segmentName) && retries <= MAX_ATTEMPTS) {
129131
SegmentFetcher fetcher = _segmentSynchronizationTaskImp.getFetcher(segmentName);
130132
try{
131133
fetcher.fetch(true);

client/src/main/java/io/split/engine/sse/workers/Worker.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public void start() {
2525
_log.debug(String.format("%s Worker starting ...", _workerName));
2626
_queue.clear();
2727
_thread = new Thread( this);
28+
_thread.setName(String.format("%s-worker", _workerName));
2829
_thread.start();
2930
} else {
3031
_log.debug(String.format("%s Worker already running.", _workerName));

client/src/test/java/io/split/TestHelper.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
44
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
55
import org.apache.hc.core5.http.ClassicHttpResponse;
6+
import org.apache.hc.core5.http.Header;
67
import org.apache.hc.core5.http.HttpEntity;
78
import org.mockito.Mockito;
89

@@ -18,7 +19,7 @@ public static CloseableHttpClient mockHttpClient(String jsonName, int httpStatus
1819
ClassicHttpResponse httpResponseMock = Mockito.mock(ClassicHttpResponse.class);
1920
Mockito.when(httpResponseMock.getEntity()).thenReturn(entityMock);
2021
Mockito.when(httpResponseMock.getCode()).thenReturn(httpStatus);
21-
22+
Mockito.when(httpResponseMock.getHeaders()).thenReturn(new Header[0]);
2223
CloseableHttpClient httpClientMock = Mockito.mock(CloseableHttpClient.class);
2324
Mockito.when(httpClientMock.execute(Mockito.anyObject())).thenReturn(classicResponseToCloseableMock(httpResponseMock));
2425

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package io.split.engine.common;
2+
3+
import org.junit.Test;
4+
5+
import java.util.stream.Collectors;
6+
import java.util.stream.Stream;
7+
8+
import static org.junit.Assert.assertEquals;
9+
import static org.junit.Assert.assertFalse;
10+
11+
public class FastlyHeadersCaptorTest {
12+
13+
@Test
14+
public void filterWorks() {
15+
FastlyHeadersCaptor captor = new FastlyHeadersCaptor();
16+
captor.handle(Stream.of(new String[][] {
17+
{"Fastly-Debug-Path", "something"},
18+
{"Fastly-Debug-TTL", "something"},
19+
{"Fastly-Debug-Digest", "something"},
20+
{"X-Served-By", "something"},
21+
{"X-Cache", "something"},
22+
{"X-Cache-Hits", "something"},
23+
{"X-Timer", "something"},
24+
{"Surrogate-Key", "something"},
25+
{"ETag", "something"},
26+
{"Cache-Control", "something"},
27+
{"X-Request-ID", "something"},
28+
{"Last-Modified", "something"},
29+
{"NON_IMPORTANT_1", "something"},
30+
{"ANOTHER_NON_IMPORTANT", "something"}
31+
}).collect(Collectors.toMap(d -> d[0], d -> d[1])));
32+
33+
assertEquals(captor.get().size(), 1);
34+
assertEquals(captor.get().get(0).size(), 12);
35+
assertFalse(captor.get().get(0).containsKey("NON_IMPORTANT_1"));
36+
assertFalse(captor.get().get(0).containsKey("ANOTHER_NON_IMPORTANT"));
37+
}
38+
39+
@Test
40+
public void orderIsPreserved() {
41+
FastlyHeadersCaptor captor = new FastlyHeadersCaptor();
42+
captor.handle(Stream.of(new String[][]{
43+
{"Fastly-Debug-Path", "first"},
44+
}).collect(Collectors.toMap(d -> d[0], d -> d[1])));
45+
46+
captor.handle(Stream.of(new String[][]{
47+
{"Fastly-Debug-Path", "second"},
48+
}).collect(Collectors.toMap(d -> d[0], d -> d[1])));
49+
50+
captor.handle(Stream.of(new String[][]{
51+
{"Fastly-Debug-Path", "third"},
52+
}).collect(Collectors.toMap(d -> d[0], d -> d[1])));
53+
54+
assertEquals(captor.get().size(), 3);
55+
assertEquals(captor.get().get(0).size(), 1);
56+
assertEquals(captor.get().get(1).size(), 1);
57+
assertEquals(captor.get().get(2).size(), 1);
58+
assertEquals(captor.get().get(0).get("Fastly-Debug-Path"), "first");
59+
assertEquals(captor.get().get(1).get("Fastly-Debug-Path"), "second");
60+
assertEquals(captor.get().get(2).get("Fastly-Debug-Path"), "third");
61+
}
62+
}

0 commit comments

Comments
 (0)