Skip to content

Commit 3c3a38d

Browse files
Merge pull request #195 from splitio/development
[4.1.4] Development to master
2 parents 42ff3e2 + c0cdc52 commit 3c3a38d

File tree

117 files changed

+3726
-2883
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

117 files changed

+3726
-2883
lines changed

client/CHANGES.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
CHANGES
22

3+
4.1.4 (Mar 19, 2021)
4+
- Updated: Internal cache structure refactor.
5+
- Updated: Streaming revamp with several bugfixes and improved log messages.
6+
- Added: Cache-Control header for on-demand requests to sdk-server.
7+
- Updated: Localhost Client revamp & bugfix for missing splits.
8+
39
4.1.3 (Dec 2, 2020)
410
- Fix Issue when closing SSE Connection
511
- Updated log-level for some messages

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.3</version>
8+
<version>4.1.4</version>
99
</parent>
1010
<artifactId>java-client</artifactId>
1111
<packaging>jar</packaging>
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package io.split.cache;
2+
3+
import com.google.common.collect.ConcurrentHashMultiset;
4+
import com.google.common.collect.Maps;
5+
import com.google.common.collect.Multiset;
6+
import com.google.common.collect.Sets;
7+
import io.split.engine.experiments.ParsedSplit;
8+
import org.slf4j.Logger;
9+
import org.slf4j.LoggerFactory;
10+
11+
import java.util.ArrayList;
12+
import java.util.Collection;
13+
import java.util.List;
14+
import java.util.concurrent.ConcurrentMap;
15+
import java.util.concurrent.atomic.AtomicLong;
16+
17+
public class InMemoryCacheImp implements SplitCache {
18+
19+
private static final Logger _log = LoggerFactory.getLogger(InMemoryCacheImp.class);
20+
21+
private final ConcurrentMap<String, ParsedSplit> _concurrentMap;
22+
private final Multiset<String> _concurrentTrafficTypeNameSet;
23+
24+
private AtomicLong _changeNumber;
25+
26+
public InMemoryCacheImp() {
27+
this(-1);
28+
}
29+
30+
public InMemoryCacheImp(long startingChangeNumber) {
31+
_concurrentMap = Maps.newConcurrentMap();
32+
_changeNumber = new AtomicLong(startingChangeNumber);
33+
_concurrentTrafficTypeNameSet = ConcurrentHashMultiset.create();
34+
}
35+
36+
@Override
37+
public void put(ParsedSplit split) {
38+
_concurrentMap.put(split.feature(), split);
39+
40+
if (split.trafficTypeName() != null) {
41+
_concurrentTrafficTypeNameSet.add(split.trafficTypeName());
42+
}
43+
}
44+
45+
@Override
46+
public boolean remove(String name) {
47+
ParsedSplit removed = _concurrentMap.remove(name);
48+
49+
if (removed != null && removed.trafficTypeName() != null) {
50+
_concurrentTrafficTypeNameSet.remove(removed.trafficTypeName());
51+
}
52+
53+
return removed != null;
54+
}
55+
56+
@Override
57+
public ParsedSplit get(String name) {
58+
return _concurrentMap.get(name);
59+
}
60+
61+
@Override
62+
public Collection<ParsedSplit> getAll() {
63+
return _concurrentMap.values();
64+
}
65+
66+
@Override
67+
public Collection<ParsedSplit> getMany(List<String> names) {
68+
List<ParsedSplit> splits = new ArrayList<>();
69+
70+
for (String name : names) {
71+
ParsedSplit split = _concurrentMap.get(name);
72+
73+
if (split != null) {
74+
splits.add(split);
75+
}
76+
}
77+
78+
return splits;
79+
}
80+
81+
@Override
82+
public long getChangeNumber() {
83+
return _changeNumber.get();
84+
}
85+
86+
@Override
87+
public void setChangeNumber(long changeNumber) {
88+
if (changeNumber < _changeNumber.get()) {
89+
_log.error("ChangeNumber for splits cache is less than previous");
90+
}
91+
92+
_changeNumber.set(changeNumber);
93+
}
94+
95+
@Override
96+
public boolean trafficTypeExists(String trafficTypeName) {
97+
// If the multiset has [{"user",2}.{"account",0}], elementSet only returns
98+
// ["user"] (it ignores "account")
99+
return Sets.newHashSet(_concurrentTrafficTypeNameSet.elementSet()).contains(trafficTypeName);
100+
}
101+
102+
@Override
103+
public void kill(String splitName, String defaultTreatment, long changeNumber) {
104+
ParsedSplit parsedSplit = _concurrentMap.get(splitName);
105+
106+
ParsedSplit updatedSplit = new ParsedSplit(parsedSplit.feature(),
107+
parsedSplit.seed(),
108+
true,
109+
defaultTreatment,
110+
parsedSplit.parsedConditions(),
111+
parsedSplit.trafficTypeName(),
112+
changeNumber,
113+
parsedSplit.trafficAllocation(),
114+
parsedSplit.trafficAllocationSeed(),
115+
parsedSplit.algo(),
116+
parsedSplit.configurations());
117+
118+
_concurrentMap.put(splitName, updatedSplit);
119+
}
120+
121+
@Override
122+
public void clear() {
123+
_concurrentMap.clear();
124+
_concurrentTrafficTypeNameSet.clear();
125+
}
126+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package io.split.cache;
2+
3+
import java.util.List;
4+
5+
/**
6+
* Memory for segments
7+
* @author lucasecheverz
8+
*/
9+
public interface SegmentCache {
10+
11+
/**
12+
* update segment
13+
* @param segmentName
14+
* @param toAdd
15+
* @param toRemove
16+
*/
17+
void updateSegment(String segmentName, List<String> toAdd, List<String> toRemove) ;
18+
19+
/**
20+
* evaluates if a key belongs to a segment
21+
* @param segmentName
22+
* @param key
23+
* @return
24+
*/
25+
boolean isInSegment(String segmentName, String key);
26+
27+
/**
28+
* update the changeNumber of a segment
29+
* @param segmentName
30+
* @param changeNumber
31+
*/
32+
void setChangeNumber(String segmentName, long changeNumber);
33+
34+
/**
35+
* returns the changeNumber of a segment
36+
* @param segmentName
37+
* @return
38+
*/
39+
long getChangeNumber(String segmentName);
40+
41+
/**
42+
* clear all segments
43+
*/
44+
void clear();
45+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package io.split.cache;
2+
3+
import com.google.common.collect.Maps;
4+
import io.split.engine.segments.SegmentImp;
5+
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
7+
8+
import java.util.List;
9+
import java.util.concurrent.ConcurrentMap;
10+
11+
/**
12+
* InMemoryCache Implementation
13+
* @author lucasecheverz
14+
*/
15+
public class SegmentCacheInMemoryImpl implements SegmentCache {
16+
private static final Logger _log = LoggerFactory.getLogger(SegmentCacheInMemoryImpl.class);
17+
private static final long DEFAULT_CHANGE_NUMBER = -1l;
18+
private final ConcurrentMap<String, SegmentImp> _segments = Maps.newConcurrentMap();
19+
20+
@Override
21+
public void updateSegment(String segmentName, List<String> toAdd, List<String> toRemove) {
22+
if(_segments.get(segmentName) == null){
23+
_segments.put(segmentName, new SegmentImp(DEFAULT_CHANGE_NUMBER, segmentName,toAdd));
24+
}
25+
26+
_segments.get(segmentName).update(toAdd,toRemove);
27+
}
28+
29+
@Override
30+
public boolean isInSegment(String segmentName, String key) {
31+
SegmentImp segmentImp = _segments.get(segmentName);
32+
if(segmentImp == null){
33+
_log.error("Segment " + segmentName + "Not found.");
34+
return false;
35+
}
36+
return segmentImp.contains(key);
37+
}
38+
39+
@Override
40+
public void setChangeNumber(String segmentName, long changeNumber) {
41+
if(_segments.get(segmentName) == null){
42+
_log.error("Segment " + segmentName + "Not found.");
43+
return ;
44+
}
45+
_segments.get(segmentName).setChangeNumber(changeNumber);
46+
}
47+
48+
@Override
49+
public long getChangeNumber(String segmentName) {
50+
SegmentImp segmentImp = _segments.get(segmentName);
51+
if(segmentImp == null){
52+
_log.error("Segment " + segmentName + "Not found.");
53+
return DEFAULT_CHANGE_NUMBER;
54+
}
55+
return segmentImp.getChangeNumber();
56+
}
57+
58+
@Override
59+
public void clear() {
60+
_segments.clear();
61+
}
62+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package io.split.cache;
2+
3+
import io.split.engine.experiments.ParsedSplit;
4+
5+
import java.util.Collection;
6+
import java.util.List;
7+
8+
public interface SplitCache {
9+
void put(ParsedSplit split);
10+
boolean remove(String name);
11+
ParsedSplit get(String name);
12+
Collection<ParsedSplit> getAll();
13+
Collection<ParsedSplit> getMany(List<String> names);
14+
long getChangeNumber();
15+
void setChangeNumber(long changeNumber);
16+
boolean trafficTypeExists(String trafficTypeName);
17+
void kill(String splitName, String defaultTreatment, long changeNumber);
18+
void clear();
19+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package io.split.client;
2+
3+
import com.google.common.annotations.VisibleForTesting;
4+
import com.google.common.collect.ConcurrentHashMultiset;
5+
import com.google.common.collect.Multiset;
6+
import org.slf4j.Logger;
7+
import org.slf4j.LoggerFactory;
8+
9+
public class ApiKeyCounter {
10+
11+
private static final Logger _log = LoggerFactory.getLogger(ApiKeyCounter.class);
12+
private static final Multiset<String> USED_API_KEYS = ConcurrentHashMultiset.create();
13+
14+
private ApiKeyCounter() {}
15+
16+
public static ApiKeyCounter getApiKeyCounterInstance() {
17+
return ApyKeyCounterHolder.INSTANCE;
18+
}
19+
20+
//Inner class to provide instance of class
21+
private static class ApyKeyCounterHolder
22+
{
23+
private static final ApiKeyCounter INSTANCE = new ApiKeyCounter();
24+
}
25+
26+
public void add(String apiKey) {
27+
String message;
28+
if (USED_API_KEYS.contains(apiKey)) {
29+
message = String.format("factory instantiation: You already have %s with this API Key. " +
30+
"We recommend keeping only one instance of the factory at all times (Singleton pattern) and reusing " +
31+
"it throughout your application.",
32+
USED_API_KEYS.count(apiKey) == 1 ? "1 factory" : String.format("%s factories", USED_API_KEYS.count(apiKey)));
33+
_log.warn(message);
34+
} else if (!USED_API_KEYS.isEmpty()) {
35+
message = "factory instantiation: You already have an instance of the Split factory. " +
36+
"Make sure you definitely want this additional instance. We recommend keeping only one instance of " +
37+
"the factory at all times (Singleton pattern) and reusing it throughout your application.“";
38+
_log.warn(message);
39+
}
40+
USED_API_KEYS.add(apiKey);
41+
}
42+
43+
public void remove(String apiKey) {
44+
USED_API_KEYS.remove(apiKey);
45+
}
46+
47+
/**
48+
* Just for test
49+
* @param apiKey
50+
* @return
51+
*/
52+
@VisibleForTesting
53+
boolean isApiKeyPresent(String apiKey) {
54+
return USED_API_KEYS.contains(apiKey);
55+
}
56+
57+
/**
58+
* Just for test
59+
* @param apiKey
60+
* @return
61+
*/
62+
@VisibleForTesting
63+
int getCount(String apiKey) {
64+
return USED_API_KEYS.count(apiKey);
65+
}
66+
}

0 commit comments

Comments
 (0)