Skip to content

Commit 78868e5

Browse files
committed
integrate all components
1 parent 74df9ee commit 78868e5

16 files changed

+357
-102
lines changed

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33

44
import io.split.client.impressions.ImpressionListener;
5+
import io.split.client.impressions.ImpressionsManager;
56
import io.split.integrations.IntegrationsConfig;
67
import org.apache.http.HttpHost;
78

@@ -24,6 +25,7 @@ public class SplitClientConfig {
2425
private final int _segmentsRefreshRate;
2526
private final int _impressionsRefreshRate;
2627
private final int _impressionsQueueSize;
28+
private final ImpressionsManager.Mode _impressionsMode;
2729
private final int _metricsRefreshRate;
2830
private final int _connectionTimeout;
2931
private final int _readTimeout;
@@ -64,6 +66,7 @@ private SplitClientConfig(String endpoint,
6466
int segmentsRefreshRate,
6567
int impressionsRefreshRate,
6668
int impressionsQueueSize,
69+
ImpressionsManager.Mode impressionsMode,
6770
int metricsRefreshRate,
6871
int connectionTimeout,
6972
int readTimeout,
@@ -93,6 +96,7 @@ private SplitClientConfig(String endpoint,
9396
_segmentsRefreshRate = segmentsRefreshRate;
9497
_impressionsRefreshRate = impressionsRefreshRate;
9598
_impressionsQueueSize = impressionsQueueSize;
99+
_impressionsMode = impressionsMode;
96100
_metricsRefreshRate = metricsRefreshRate;
97101
_connectionTimeout = connectionTimeout;
98102
_readTimeout = readTimeout;
@@ -158,6 +162,8 @@ public int impressionsQueueSize() {
158162
return _impressionsQueueSize;
159163
}
160164

165+
public ImpressionsManager.Mode impressionsMode() { return _impressionsMode; }
166+
161167
public int metricsRefreshRate() {
162168
return _metricsRefreshRate;
163169
}
@@ -252,6 +258,7 @@ public static final class Builder {
252258
private int _segmentsRefreshRate = 60;
253259
private int _impressionsRefreshRate = 30;
254260
private int _impressionsQueueSize = 30000;
261+
private ImpressionsManager.Mode _impressionsMode = ImpressionsManager.Mode.OPTIMIZED;
255262
private int _connectionTimeout = 15000;
256263
private int _readTimeout = 15000;
257264
private int _numThreadsForSegmentFetch = 2;
@@ -380,6 +387,11 @@ public Builder impressionsRefreshRate(int seconds) {
380387
return this;
381388
}
382389

390+
public Builder impressionsMode(ImpressionsManager.Mode mode) {
391+
_impressionsMode = mode;
392+
return this;
393+
}
394+
383395
/**
384396
* The impression listener captures the which key saw what treatment ("on", "off", etc)
385397
* at what time. This log is periodically pushed back to split endpoint.
@@ -734,6 +746,7 @@ public SplitClientConfig build() {
734746
_segmentsRefreshRate,
735747
_impressionsRefreshRate,
736748
_impressionsQueueSize,
749+
_impressionsMode,
737750
_metricsRefreshRate,
738751
_connectionTimeout,
739752
_readTimeout,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
183183
}
184184

185185
// Impressions
186-
final ImpressionsManagerImpl impressionsManager = ImpressionsManagerImpl.instance(httpclient, config, impressionListeners);
186+
final ImpressionsManagerImpl impressionsManager = ImpressionsManagerImpl.instance(httpclient, config, impressionListeners, config.impressionsMode());
187187

188188
CachedMetrics cachedMetrics = new CachedMetrics(httpMetrics, TimeUnit.SECONDS.toMillis(config.metricsRefreshRate()));
189189
final FireAndForgetMetrics cachedFireAndForgetMetrics = FireAndForgetMetrics.instance(cachedMetrics, 2, 1000);
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package io.split.client.dtos;
2+
3+
import io.split.client.impressions.ImpressionCounter;
4+
import java.util.List;
5+
import java.util.Map;
6+
import java.util.stream.Collectors;
7+
8+
public class ImpressionCount {
9+
10+
public final List<CountPerFeature> counts;
11+
12+
public ImpressionCount(List<CountPerFeature> cs) {
13+
counts = cs;
14+
}
15+
16+
public static ImpressionCount fromImpressionCounterData(Map<ImpressionCounter.Key, Integer> raw) {
17+
return new ImpressionCount(raw.entrySet().stream()
18+
.map(e -> new CountPerFeature(e.getKey().featureName(), e.getKey().timeFrame(), e.getValue()))
19+
.collect(Collectors.toList()));
20+
}
21+
22+
public static class CountPerFeature {
23+
public final String feature;
24+
public final long timeframe;
25+
public final int count;
26+
27+
public CountPerFeature(String f, long t, int c) {
28+
feature = f;
29+
timeframe = t;
30+
count = c;
31+
}
32+
}
33+
}

client/src/main/java/io/split/client/dtos/KeyImpression.java

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,43 @@
11
package io.split.client.dtos;
22

33

4+
import com.google.gson.annotations.SerializedName;
45
import io.split.client.impressions.Impression;
56

7+
import java.util.Objects;
8+
69
public class KeyImpression {
7-
public String feature;
10+
11+
/* package private */ static final String FIELD_KEY_NAME = "k";
12+
/* package private */ static final String FIELD_BUCKETING_KEY = "b";
13+
/* package private */ static final String FIELD_TREATMENT = "t";
14+
/* package private */ static final String FIELD_LABEL = "r";
15+
/* package private */ static final String FIELD_TIME = "m";
16+
/* package private */ static final String FIELD_CHANGE_NUMBER = "c";
17+
/* package private */ static final String FIELD_PREVIOUS_TIME = "pt";
18+
19+
public transient String feature; // Non-serializable
20+
21+
@SerializedName(FIELD_KEY_NAME)
822
public String keyName;
23+
24+
@SerializedName(FIELD_BUCKETING_KEY)
925
public String bucketingKey;
26+
27+
@SerializedName(FIELD_TREATMENT)
1028
public String treatment;
29+
30+
@SerializedName(FIELD_LABEL)
1131
public String label;
32+
33+
@SerializedName(FIELD_TIME)
1234
public long time;
35+
36+
@SerializedName(FIELD_CHANGE_NUMBER)
1337
public Long changeNumber; // can be null if there is no changeNumber
14-
public Long pt;
38+
39+
@SerializedName(FIELD_PREVIOUS_TIME)
40+
public Long previousTime;
1541

1642
@Override
1743
public boolean equals(Object o) {
@@ -21,7 +47,7 @@ public boolean equals(Object o) {
2147
KeyImpression that = (KeyImpression) o;
2248

2349
if (time != that.time) return false;
24-
if (feature != null ? !feature.equals(that.feature) : that.feature != null) return false;
50+
if (!Objects.equals(feature, that.feature)) return false;
2551
if (!keyName.equals(that.keyName)) return false;
2652
if (!treatment.equals(that.treatment)) return false;
2753

client/src/main/java/io/split/client/dtos/TestImpressions.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
package io.split.client.dtos;
22

3+
import com.google.gson.annotations.SerializedName;
4+
35
import java.util.List;
46
import java.util.stream.Collectors;
57

68
public class TestImpressions {
9+
10+
/* package private */ static final String FIELD_TEST_NAME = "f";
11+
/* package private */ static final String FIELD_KEY_IMPRESSIONS = "i";
12+
13+
@SerializedName(FIELD_TEST_NAME)
714
public String testName;
15+
16+
@SerializedName(FIELD_KEY_IMPRESSIONS)
817
public List<KeyImpression> keyImpressions;
918

1019
public TestImpressions(String testName_, List<KeyImpression> keyImpressions_) {

client/src/main/java/io/split/client/impressions/HttpImpressionsSender.java

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,59 @@
11
package io.split.client.impressions;
22

33
import com.google.common.annotations.VisibleForTesting;
4+
import io.split.client.dtos.ImpressionCount;
45
import io.split.client.dtos.TestImpressions;
56
import io.split.client.utils.Utils;
7+
68
import org.apache.http.client.methods.CloseableHttpResponse;
79
import org.apache.http.client.methods.HttpPost;
810
import org.apache.http.entity.StringEntity;
911
import org.apache.http.impl.client.CloseableHttpClient;
1012
import org.slf4j.Logger;
1113
import org.slf4j.LoggerFactory;
1214

15+
import java.io.IOException;
1316
import java.net.URI;
1417
import java.net.URISyntaxException;
18+
import java.util.HashMap;
1519
import java.util.List;
1620

1721
/**
1822
* Created by patricioe on 6/20/16.
1923
*/
2024
public class HttpImpressionsSender implements ImpressionsSender {
2125

26+
private static final String BULK_ENDPOINT_PATH = "api/testImpressions/bulk";
27+
private static final String COUNT_ENDPOINT_PATH = "api/testImpressions/count";
28+
2229
private static final Logger _logger = LoggerFactory.getLogger(HttpImpressionsSender.class);
2330

24-
private CloseableHttpClient _client;
25-
private URI _target;
31+
private final CloseableHttpClient _client;
32+
private final URI _impressionBulkTarget;
33+
private final URI _impressionCountTarget;
2634

2735

2836
public static HttpImpressionsSender create(CloseableHttpClient client, URI eventsRootEndpoint) throws URISyntaxException {
29-
return new HttpImpressionsSender(client, Utils.appendPath(eventsRootEndpoint, "api/testImpressions/bulk"));
37+
return new HttpImpressionsSender(client,
38+
Utils.appendPath(eventsRootEndpoint, BULK_ENDPOINT_PATH),
39+
Utils.appendPath(eventsRootEndpoint, COUNT_ENDPOINT_PATH));
3040
}
3141

32-
private HttpImpressionsSender(CloseableHttpClient client, URI target) throws URISyntaxException {
42+
private HttpImpressionsSender(CloseableHttpClient client, URI impressionBulkTarget, URI impressionCountTarget) {
3343
_client = client;
34-
_target = target;
44+
_impressionBulkTarget = impressionBulkTarget;
45+
_impressionCountTarget = impressionCountTarget;
3546
}
3647

3748
@Override
38-
public void post(List<TestImpressions> impressions) {
49+
public void postImpressionsBulk(List<TestImpressions> impressions) {
3950

4051
CloseableHttpResponse response = null;
4152

4253
try {
4354
StringEntity entity = Utils.toJsonEntity(impressions);
4455

45-
HttpPost request = new HttpPost(_target);
56+
HttpPost request = new HttpPost(_impressionBulkTarget);
4657
request.setEntity(entity);
4758

4859
response = _client.execute(request);
@@ -61,8 +72,23 @@ public void post(List<TestImpressions> impressions) {
6172

6273
}
6374

75+
@Override
76+
public void postCounters(HashMap<ImpressionCounter.Key, Integer> raw) {
77+
HttpPost request = new HttpPost(_impressionCountTarget);
78+
request.setEntity(Utils.toJsonEntity(ImpressionCount.fromImpressionCounterData(raw)));
79+
try (CloseableHttpResponse response = _client.execute(request)) {
80+
int status = response.getStatusLine().getStatusCode();
81+
82+
if (status < 200 || status >= 300) {
83+
_logger.warn("Response status was: " + status);
84+
}
85+
} catch (IOException exc) {
86+
_logger.warn("Exception when posting impression counters: ", exc);
87+
}
88+
}
89+
6490
@VisibleForTesting
6591
URI getTarget() {
66-
return _target;
92+
return _impressionBulkTarget;
6793
}
6894
}

client/src/main/java/io/split/client/impressions/ImpressionCounter.java

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,46 @@
55
import java.util.concurrent.ConcurrentHashMap;
66
import java.util.concurrent.atomic.AtomicInteger;
77

8+
import static com.google.common.base.Preconditions.checkNotNull;
9+
810
public class ImpressionCounter {
911

10-
private static final long TIME_INTERVAL_MS = 3600L * 1000L;
12+
public static class Key {
13+
private final String _featureName;
14+
private final long _timeFrame;
15+
16+
public Key(String featureName, long timeframe) {
17+
_featureName = checkNotNull(featureName);
18+
_timeFrame = timeframe;
19+
}
20+
21+
public String featureName() { return _featureName; }
22+
public long timeFrame() { return _timeFrame; }
23+
24+
@Override
25+
public int hashCode() {
26+
return Objects.hash(_featureName, _timeFrame);
27+
}
28+
29+
@Override
30+
public boolean equals(Object o) {
31+
if (this == o) return true;
32+
if (o == null || getClass() != o.getClass()) return false;
1133

12-
private final ConcurrentHashMap<String, AtomicInteger> _counts;
34+
Key key = (Key) o;
35+
return Objects.equals(_featureName, key._featureName) && Objects.equals(_timeFrame, key._timeFrame);
36+
}
37+
}
38+
39+
40+
private final ConcurrentHashMap<Key, AtomicInteger> _counts;
1341

1442
public ImpressionCounter() {
1543
_counts = new ConcurrentHashMap<>();
1644
}
1745

1846
public void inc(String featureName, long timeFrame, int amount) {
19-
String key = makeKey(featureName, timeFrame);
47+
Key key = new Key(featureName, ImpressionUtils.truncateTimeframe(timeFrame));
2048
AtomicInteger count = _counts.get(key);
2149
if (Objects.isNull(count)) {
2250
count = new AtomicInteger();
@@ -28,20 +56,14 @@ public void inc(String featureName, long timeFrame, int amount) {
2856
count.addAndGet(amount);
2957
}
3058

31-
public HashMap<String, Integer> popAll() {
32-
HashMap<String, Integer> toReturn = new HashMap<>();
33-
for (String key : _counts.keySet()) {
59+
public HashMap<Key, Integer> popAll() {
60+
HashMap<Key, Integer> toReturn = new HashMap<>();
61+
for (Key key : _counts.keySet()) {
3462
AtomicInteger curr = _counts.remove(key);
35-
toReturn.put(key ,curr.get());
63+
toReturn.put(key, curr.get());
3664
}
3765
return toReturn;
3866
}
3967

40-
static String makeKey(String featureName, long timeFrame) {
41-
return String.join("::", featureName, String.valueOf(truncateTimeframe(timeFrame)));
42-
}
43-
44-
static long truncateTimeframe(long timestampInMs) {
45-
return timestampInMs - (timestampInMs % TIME_INTERVAL_MS);
46-
}
68+
public boolean isEmpty() { return _counts.isEmpty(); }
4769
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package io.split.client.impressions;
2+
3+
public class ImpressionUtils {
4+
5+
private static final long TIME_INTERVAL_MS = 3600L * 1000L;
6+
7+
public static long truncateTimeframe(long timestampInMs) {
8+
return timestampInMs - (timestampInMs % TIME_INTERVAL_MS);
9+
}
10+
}

client/src/main/java/io/split/client/impressions/ImpressionsManager.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
package io.split.client.impressions;
22

33
public interface ImpressionsManager {
4+
5+
public enum Mode {
6+
OPTIMIZED,
7+
DEBUG
8+
}
9+
410
void track(Impression impression);
511

612
final class NoOpImpressionsManager implements ImpressionsManager {

0 commit comments

Comments
 (0)