Skip to content

Commit 8788b77

Browse files
committed
support for cdn response headers logging - wip
1 parent 6541254 commit 8788b77

File tree

15 files changed

+267
-29
lines changed

15 files changed

+267
-29
lines changed

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

Lines changed: 20 additions & 5 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

@@ -28,8 +32,12 @@ public final class HttpSplitChangeFetcher implements SplitChangeFetcher {
2832

2933
private static final String SINCE = "since";
3034
private static final String PREFIX = "splitChangeFetcher";
31-
private static final String NAME_CACHE = "Cache-Control";
32-
private static final String VALUE_CACHE = "no-cache";
35+
36+
private static final String HEADER_CACHE_CONTROL_NAME = "Cache-Control";
37+
private static final String HEADER_CACHE_CONTROL_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,7 +59,7 @@ private HttpSplitChangeFetcher(CloseableHttpClient client, URI uri, Metrics metr
5159
}
5260

5361
@Override
54-
public SplitChange fetch(long since, boolean addCacheHeader) {
62+
public SplitChange fetch(long since, FetchOptions options) {
5563

5664
long start = System.currentTimeMillis();
5765

@@ -61,10 +69,17 @@ public SplitChange fetch(long since, boolean addCacheHeader) {
6169
URI uri = new URIBuilder(_target).addParameter(SINCE, "" + since).build();
6270

6371
HttpGet request = new HttpGet(uri);
64-
if(addCacheHeader) {
65-
request.setHeader(NAME_CACHE, VALUE_CACHE);
72+
if(options.cacheControlHeadersEnabled()) {
73+
request.setHeader(HEADER_CACHE_CONTROL_NAME, HEADER_CACHE_CONTROL_VALUE);
6674
}
75+
76+
if (options.fastlyDebugHeaderEnabled()) {
77+
request.addHeader(HEADER_FASTLY_DEBUG_NAME, HEADER_FASTLY_DEBUG_VALUE);
78+
}
79+
6780
response = _client.execute(request);
81+
options.handleResponseHeaders(Arrays.stream(response.getHeaders())
82+
.collect(Collectors.toMap(Header::getName, Header::getValue)));
6883

6984
int statusCode = response.getCode();
7085

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

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public class SplitClientConfig {
4747
private final String _authServiceURL;
4848
private final String _streamingServiceURL;
4949
private final int _onDemandFetchRetryDelayMs;
50+
private final boolean _cdnDebugLogging;
5051

5152
// Proxy configs
5253
private final HttpHost _proxy;
@@ -91,7 +92,8 @@ private SplitClientConfig(String endpoint,
9192
int streamingReconnectBackoffBase,
9293
String authServiceURL,
9394
String streamingServiceURL,
94-
int onDemandFetchRetryDelayMs) {
95+
int onDemandFetchRetryDelayMs,
96+
boolean cdnDebugLogging) {
9597
_endpoint = endpoint;
9698
_eventsEndpoint = eventsEndpoint;
9799
_featuresRefreshRate = pollForFeatureChangesEveryNSeconds;
@@ -123,6 +125,7 @@ private SplitClientConfig(String endpoint,
123125
_authServiceURL = authServiceURL;
124126
_streamingServiceURL = streamingServiceURL;
125127
_onDemandFetchRetryDelayMs = onDemandFetchRetryDelayMs;
128+
_cdnDebugLogging = cdnDebugLogging;
126129

127130
Properties props = new Properties();
128131
try {
@@ -289,6 +292,7 @@ public static final class Builder {
289292
private String _authServiceURL = "https://auth.split.io/api/auth";
290293
private String _streamingServiceURL = "https://streaming.split.io/sse";
291294
private int _onDemandFetchRetryDelayMs = 50;
295+
private boolean _cdnDebugLogging = false;
292296

293297
public Builder() {
294298
}
@@ -690,6 +694,16 @@ public Builder streamingRetryDelay(int onDemandFetchRetryDelayMs) {
690694
return this;
691695
}
692696

697+
/**
698+
* Enable logging response headers for requests made to our CDN.
699+
* @param cdnDebugLogging
700+
* @return
701+
*/
702+
public Builder cdnDebugLogging(boolean cdnDebugLogging) {
703+
_cdnDebugLogging = cdnDebugLogging;
704+
return this;
705+
}
706+
693707
public SplitClientConfig build() {
694708
if (_featuresRefreshRate < 5 ) {
695709
throw new IllegalArgumentException("featuresRefreshRate must be >= 5: " + _featuresRefreshRate);
@@ -795,7 +809,8 @@ public SplitClientConfig build() {
795809
_streamingReconnectBackoffBase,
796810
_authServiceURL,
797811
_streamingServiceURL,
798-
_onDemandFetchRetryDelayMs);
812+
_onDemandFetchRetryDelayMs,
813+
_cdnDebugLogging);
799814
}
800815
}
801816
}

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,6 @@ public boolean isDestroyed() {
207207
}
208208

209209
private static CloseableHttpClient buildHttpClient(String apiToken, SplitClientConfig config) {
210-
211210
SSLConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactoryBuilder.create()
212211
.setSslContext(SSLContexts.createSystemDefault())
213212
.setTlsVersions(TLS.V_1_1, TLS.V_1_2)

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

Lines changed: 2 additions & 1 deletion
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
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package io.split.engine.common;
2+
3+
import java.util.*;
4+
import java.util.stream.Collectors;
5+
6+
public class FastlyHeadersCaptor {
7+
8+
public static Set<String> FIELDS_TO_CAPTURE = new HashSet<>(Arrays.asList(
9+
"Fastly-Debug-Path",
10+
"Fastly-Debug-TTL",
11+
"Fastly-Debug-Digest",
12+
"X-Served-By",
13+
"X-Cache",
14+
"X-Cache-Hits",
15+
"X-Timer",
16+
"Surrogate-Key",
17+
"ETag",
18+
"Cache-Control",
19+
"X-Request-ID",
20+
"Last-Modified"
21+
));
22+
23+
private final List<Map<String, String>> _headers = new ArrayList<>();
24+
25+
public Void handle(Map<String, String> responseHeaders) {
26+
_headers.add(responseHeaders.entrySet().stream()
27+
.filter(e -> FIELDS_TO_CAPTURE.contains(e.getKey()))
28+
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
29+
return null;
30+
}
31+
32+
public List<Map<String, String>> get() {
33+
return _headers;
34+
}
35+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package io.split.engine.common;
2+
3+
import java.util.Map;
4+
import java.util.Objects;
5+
import java.util.function.Function;
6+
7+
public class FetchOptions {
8+
9+
public static class Builder {
10+
public Builder() {}
11+
12+
public Builder cacheControlHeaders(boolean on) {
13+
_cacheControlHeaders = on;
14+
return this;
15+
}
16+
17+
public Builder fastlyDebugHeader(boolean on) {
18+
_fastlyDebugHeader = on;
19+
return this;
20+
}
21+
22+
public Builder responseHeadersCallback(Function<Map<String, String>, Void> callback) {
23+
_responseHeadersCallback = callback;
24+
return this;
25+
}
26+
27+
public FetchOptions build() {
28+
return new FetchOptions(_cacheControlHeaders, _responseHeadersCallback, _fastlyDebugHeader);
29+
}
30+
31+
private boolean _cacheControlHeaders = false;
32+
private boolean _fastlyDebugHeader = false;
33+
private Function<Map<String, String>, Void> _responseHeadersCallback = null;
34+
}
35+
36+
public boolean cacheControlHeadersEnabled() {
37+
return _cacheControlHeaders;
38+
}
39+
40+
public boolean fastlyDebugHeaderEnabled() {
41+
return _fastlyDebugHeader;
42+
}
43+
44+
public void handleResponseHeaders(Map<String, String> headers) {
45+
if (Objects.isNull(_responseHeadersCallback) || Objects.isNull(headers)) {
46+
return;
47+
}
48+
_responseHeadersCallback.apply(headers);
49+
}
50+
51+
private FetchOptions(boolean cacheControlHeaders, Function<Map<String, String>, Void> responseHeadersCallback,
52+
boolean fastlyDebugHeader) {
53+
_cacheControlHeaders = cacheControlHeaders;
54+
_responseHeadersCallback = responseHeadersCallback;
55+
_fastlyDebugHeader = fastlyDebugHeader;
56+
}
57+
58+
private final boolean _cacheControlHeaders;
59+
private final boolean _fastlyDebugHeader;
60+
private final Function<Map<String, String>, Void> _responseHeadersCallback;
61+
}

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

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

33
import com.google.common.util.concurrent.ThreadFactoryBuilder;
4+
import com.google.gson.Gson;
5+
import com.google.gson.GsonBuilder;
46
import io.split.cache.SegmentCache;
57
import io.split.cache.SplitCache;
68
import io.split.engine.experiments.SplitFetcher;
@@ -10,6 +12,9 @@
1012
import org.slf4j.Logger;
1113
import org.slf4j.LoggerFactory;
1214

15+
import java.util.ArrayList;
16+
import java.util.List;
17+
import java.util.Map;
1318
import java.util.concurrent.Executors;
1419
import java.util.concurrent.ScheduledExecutorService;
1520
import java.util.concurrent.ThreadFactory;
@@ -21,13 +26,15 @@ public class SynchronizerImp implements Synchronizer {
2126
private static final Logger _log = LoggerFactory.getLogger(Synchronizer.class);
2227
private static final int RETRIES_NUMBER = 10;
2328

29+
private final boolean _cdnResponseHeadersLogging = true;
2430
private final SplitSynchronizationTask _splitSynchronizationTask;
2531
private final SplitFetcher _splitFetcher;
2632
private final SegmentSynchronizationTask _segmentSynchronizationTaskImp;
2733
private final ScheduledExecutorService _syncAllScheduledExecutorService;
2834
private final SplitCache _splitCache;
2935
private final SegmentCache _segmentCache;
3036
private final int _onDemandFetchRetryDelayMs;
37+
private final Gson gson = new GsonBuilder().create();
3138

3239
public SynchronizerImp(SplitSynchronizationTask splitSynchronizationTask,
3340
SplitFetcher splitFetcher,
@@ -52,7 +59,7 @@ public SynchronizerImp(SplitSynchronizationTask splitSynchronizationTask,
5259
@Override
5360
public void syncAll() {
5461
_syncAllScheduledExecutorService.schedule(() -> {
55-
_splitFetcher.fetchAll(true);
62+
_splitFetcher.fetchAll(new FetchOptions.Builder().cacheControlHeaders(true).build());
5663
_segmentSynchronizationTaskImp.fetchAll(true);
5764
}, 0, TimeUnit.SECONDS);
5865
}
@@ -74,14 +81,28 @@ public void stopPeriodicFetching() {
7481
@Override
7582
public void refreshSplits(long targetChangeNumber) {
7683
int retries = RETRIES_NUMBER;
77-
while(targetChangeNumber > _splitCache.getChangeNumber()) {
84+
if (targetChangeNumber <= _splitCache.getChangeNumber()) {
85+
return;
86+
}
87+
88+
FastlyHeadersCaptor captor = new FastlyHeadersCaptor();
89+
FetchOptions opts = new FetchOptions.Builder()
90+
.cacheControlHeaders(true)
91+
.fastlyDebugHeader(_cdnResponseHeadersLogging)
92+
.responseHeadersCallback(_cdnResponseHeadersLogging ? captor::handle : null)
93+
.build();
94+
95+
while(true) {
7896
retries--;
79-
_splitFetcher.forceRefresh(true);
97+
_splitFetcher.forceRefresh(opts);
8098
if (targetChangeNumber <= _splitCache.getChangeNumber()) {
8199
_log.debug("Refresh completed in %s attempts.", RETRIES_NUMBER - retries);
82100
return;
83101
} else if (retries <= 0) {
84102
_log.warn("No changes fetched after %s attempts.", RETRIES_NUMBER);
103+
if (_cdnResponseHeadersLogging) {
104+
_log.debug("CDN Debug headers: ", gson.toJson(captor.get()));
105+
}
85106
return;
86107
}
87108
try {

client/src/main/java/io/split/engine/experiments/SplitChangeFetcher.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.split.engine.experiments;
22

33
import io.split.client.dtos.SplitChange;
4+
import io.split.engine.common.FetchOptions;
45

56
/**
67
* Created by adilaijaz on 5/11/15.
@@ -31,5 +32,5 @@ public interface SplitChangeFetcher {
3132
* @return SegmentChange
3233
* @throws java.lang.RuntimeException if there was a problem computing split changes
3334
*/
34-
SplitChange fetch(long since, boolean addCacheHeader);
35+
SplitChange fetch(long since, FetchOptions options);
3536
}
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.split.engine.experiments;
22

3+
import io.split.engine.common.FetchOptions;
4+
35
/**
46
* Created by adilaijaz on 5/8/15.
57
*/
@@ -8,11 +10,11 @@ public interface SplitFetcher extends Runnable {
810
* Forces a sync of splits, outside of any scheduled
911
* syncs. This method MUST NOT throw any exceptions.
1012
*/
11-
void forceRefresh(boolean addCacheHeader);
13+
void forceRefresh(FetchOptions options);
1214

1315
/**
1416
* Forces a sync of ALL splits, outside of any scheduled
1517
* syncs. This method MUST NOT throw any exceptions.
1618
*/
17-
void fetchAll(boolean addCacheHeader);
19+
void fetchAll(FetchOptions options);
1820
}

0 commit comments

Comments
 (0)