Skip to content

Commit 0bc7d56

Browse files
committed
traffic type exists refactor
1 parent af68d6a commit 0bc7d56

File tree

7 files changed

+150
-217
lines changed

7 files changed

+150
-217
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ private boolean track(Event event) {
192192
event.trafficTypeName = event.trafficTypeName.toLowerCase();
193193
}
194194

195-
if (!_splitFetcher.fetchKnownTrafficTypes().contains(event.trafficTypeName)) {
195+
if (!_splitFetcher.trafficTypeExists(event.trafficTypeName)) {
196196
_log.warn("track: Traffic Type " + event.trafficTypeName + " does not have any corresponding Splits in this environment, " +
197197
"make sure you’re tracking your events to a valid traffic type defined in the Split console.");
198198
}

client/src/main/java/io/split/engine/cache/InMemoryCacheImp.java

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
package io.split.engine.cache;
22

3+
import com.google.common.collect.ConcurrentHashMultiset;
34
import com.google.common.collect.Maps;
5+
import com.google.common.collect.Multiset;
6+
import com.google.common.collect.Sets;
47
import io.split.engine.experiments.ParsedSplit;
58
import org.slf4j.Logger;
69
import org.slf4j.LoggerFactory;
710

811
import java.util.ArrayList;
912
import java.util.Collection;
1013
import java.util.List;
11-
import java.util.Map;
14+
import java.util.Set;
1215
import java.util.concurrent.ConcurrentMap;
1316
import java.util.concurrent.atomic.AtomicLong;
1417

@@ -17,27 +20,37 @@ public class InMemoryCacheImp implements SplitCache {
1720
private static final Logger _log = LoggerFactory.getLogger(InMemoryCacheImp.class);
1821

1922
private final ConcurrentMap<String, ParsedSplit> _concurrentMap;
23+
private final Multiset<String> _concurrentTrafficTypeNameSet;
24+
2025
private AtomicLong _changeNumber;
2126

27+
public InMemoryCacheImp() {
28+
this(-1);
29+
}
30+
2231
public InMemoryCacheImp(long startingChangeNumber) {
2332
_concurrentMap = Maps.newConcurrentMap();
2433
_changeNumber = new AtomicLong(startingChangeNumber);
34+
_concurrentTrafficTypeNameSet = ConcurrentHashMultiset.create();
2535
}
2636

2737
@Override
2838
public void put(ParsedSplit split) {
2939
_concurrentMap.put(split.feature(), split);
30-
}
3140

32-
@Override
33-
public void putAll(Map<String, ParsedSplit> splits) {
34-
_concurrentMap.putAll(splits);
41+
if (split.trafficTypeName() != null) {
42+
_concurrentTrafficTypeNameSet.add(split.trafficTypeName());
43+
}
3544
}
3645

3746
@Override
3847
public boolean remove(String name) {
3948
ParsedSplit removed = _concurrentMap.remove(name);
4049

50+
if (removed != null && removed.trafficTypeName() != null) {
51+
_concurrentTrafficTypeNameSet.remove(removed.trafficTypeName());
52+
}
53+
4154
return removed != null;
4255
}
4356

@@ -81,12 +94,15 @@ public void setChangeNumber(long changeNumber) {
8194
}
8295

8396
@Override
84-
public boolean trafficTypeExists(String trafficType) {
85-
return false;
97+
public boolean trafficTypeExists(String trafficTypeName) {
98+
// If the multiset has [{"user",2}.{"account",0}], elementSet only returns
99+
// ["user"] (it ignores "account")
100+
return Sets.newHashSet(_concurrentTrafficTypeNameSet.elementSet()).contains(trafficTypeName);
86101
}
87102

88103
@Override
89104
public void clear() {
90105
_concurrentMap.clear();
106+
_concurrentTrafficTypeNameSet.clear();
91107
}
92108
}

client/src/main/java/io/split/engine/cache/SplitCache.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,16 @@
44

55
import java.util.Collection;
66
import java.util.List;
7-
import java.util.Map;
7+
import java.util.Set;
88

99
public interface SplitCache {
1010
void put(ParsedSplit split);
11-
void putAll(Map<String, ParsedSplit> splits);
1211
boolean remove(String name);
1312
ParsedSplit get(String name);
1413
Collection<ParsedSplit> getAll();
1514
Collection<ParsedSplit> getMany(List<String> names);
1615
long getChangeNumber();
1716
void setChangeNumber(long changeNumber);
18-
boolean trafficTypeExists(String trafficType);
17+
boolean trafficTypeExists(String trafficTypeName);
1918
void clear();
2019
}

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

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

3-
import com.google.common.collect.ConcurrentHashMultiset;
43
import com.google.common.collect.Lists;
5-
import com.google.common.collect.Maps;
6-
import com.google.common.collect.Multiset;
7-
import com.google.common.collect.Multisets;
8-
import com.google.common.collect.Sets;
94
import io.split.client.dtos.Split;
105
import io.split.client.dtos.SplitChange;
116
import io.split.client.dtos.Status;
@@ -16,8 +11,6 @@
1611

1712
import java.util.Collection;
1813
import java.util.List;
19-
import java.util.Map;
20-
import java.util.Set;
2114

2215
import static com.google.common.base.Preconditions.checkNotNull;
2316

@@ -45,7 +38,6 @@ public class RefreshableSplitFetcher implements SplitFetcher, Runnable {
4538
* The count is used to maintain how many splits are using a traffic type, so when
4639
* an ARCHIVED split is received, we know if we need to remove a traffic type from the multiset.
4740
*/
48-
Multiset<String> _concurrentTrafficTypeNameSet = ConcurrentHashMultiset.create();
4941

5042
public RefreshableSplitFetcher(SplitChangeFetcher splitChangeFetcher, SplitParser parser, SDKReadinessGates gates, SplitCache splitCache) {
5143
_splitChangeFetcher = checkNotNull(splitChangeFetcher);
@@ -111,11 +103,8 @@ public List<ParsedSplit> fetchAll() {
111103
}
112104

113105
@Override
114-
public Set<String> fetchKnownTrafficTypes() {
115-
// We return the "keys" of the multiset that have a count greater than 0
116-
// If the multiset has [{"user",2}.{"account",0}], elementSet only returns
117-
// ["user"] (it ignores "account")
118-
return Sets.newHashSet(_concurrentTrafficTypeNameSet.elementSet());
106+
public boolean trafficTypeExists(String trafficTypeName) {
107+
return _splitCache.trafficTypeExists(trafficTypeName);
119108
}
120109

121110
public Collection<ParsedSplit> fetch() {
@@ -124,7 +113,6 @@ public Collection<ParsedSplit> fetch() {
124113

125114
public void clear() {
126115
_splitCache.clear();
127-
_concurrentTrafficTypeNameSet.clear();
128116
}
129117

130118
@Override
@@ -180,68 +168,40 @@ public void runWithoutExceptionHandling() throws InterruptedException {
180168
return;
181169
}
182170

183-
Set<String> toRemove = Sets.newHashSet();
184-
Map<String, ParsedSplit> toAdd = Maps.newHashMap();
185-
List<String> trafficTypeNamesToRemove = Lists.newArrayList();
186-
List<String> trafficTypeNamesToAdd = Lists.newArrayList();
187-
188171
for (Split split : change.splits) {
189172
if (Thread.currentThread().isInterrupted()) {
190173
throw new InterruptedException();
191174
}
192175

193176
if (split.status != Status.ACTIVE) {
194177
// archive.
195-
toRemove.add(split.name);
196-
if (split.trafficTypeName != null) {
197-
trafficTypeNamesToRemove.add(split.trafficTypeName);
198-
}
178+
_splitCache.remove(split.name);
199179
continue;
200180
}
201181

202182
ParsedSplit parsedSplit = _parser.parse(split);
203183
if (parsedSplit == null) {
204184
_log.info("We could not parse the experiment definition for: " + split.name + " so we are removing it completely to be careful");
205-
toRemove.add(split.name);
206-
if (split.trafficTypeName != null) {
207-
trafficTypeNamesToRemove.add(split.trafficTypeName);
208-
}
185+
186+
_splitCache.remove(split.name);
187+
_log.debug("Deleted feature: " + split.name);
188+
209189
continue;
210190
}
211191

212-
toAdd.put(split.name, parsedSplit);
213-
214192
// If the split already exists, this is either an update, or the split has been
215193
// deleted and recreated (possibly with a different traffic type).
216194
// If it's an update, the traffic type should NOT be increased.
217195
// If it's deleted & recreated, the old one should be decreased and the new one increased.
218196
// To handle both cases, we simply delete the old one if the split is present.
219197
// The new one is always increased.
220198
ParsedSplit current = _splitCache.get(split.name);
221-
if (current != null && current.trafficTypeName() != null) {
222-
trafficTypeNamesToRemove.add(current.trafficTypeName());
223-
}
224-
225-
if (split.trafficTypeName != null) {
226-
trafficTypeNamesToAdd.add(split.trafficTypeName);
199+
if (current != null) {
200+
_splitCache.remove(split.name);
227201
}
228-
}
229-
230-
_splitCache.putAll(toAdd);
231-
_concurrentTrafficTypeNameSet.addAll(trafficTypeNamesToAdd);
232-
//removeAll does not work here, since it wont remove all the occurrences, just one
233-
Multisets.removeOccurrences(_concurrentTrafficTypeNameSet, trafficTypeNamesToRemove);
234-
235-
for (String remove : toRemove) {
236-
_splitCache.remove(remove);
237-
}
238-
239-
if (!toAdd.isEmpty()) {
240-
_log.debug("Updated features: " + toAdd.keySet());
241-
}
242202

243-
if (!toRemove.isEmpty()) {
244-
_log.debug("Deleted features: " + toRemove);
203+
_splitCache.put(parsedSplit);
204+
_log.debug("Updated feature: " + parsedSplit.feature());
245205
}
246206

247207
_splitCache.setChangeNumber(change.till);

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

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

33
import java.util.List;
4-
import java.util.Set;
54

65
/**
76
* Created by adilaijaz on 5/8/15.
@@ -11,15 +10,7 @@ public interface SplitFetcher {
1110

1211
List<ParsedSplit> fetchAll();
1312

14-
/**
15-
* Fetches all the traffic types that are being used by the splits that are currently stored.
16-
*
17-
* For example, if the fetcher currently contains three splits, one of traffic type "account"
18-
* and two of traffic type "user", this method will return ["account", "user"]
19-
*
20-
* @return a set of all the traffic types used by the parsed splits
21-
*/
22-
Set<String> fetchKnownTrafficTypes();
13+
boolean trafficTypeExists(String trafficTypeName);
2314

2415
/**
2516
* Forces a sync of splits, outside of any scheduled
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package io.split.engine.cache;
2+
3+
import io.split.engine.experiments.ParsedSplit;
4+
import org.junit.Assert;
5+
import org.junit.Before;
6+
import org.junit.Test;
7+
8+
import java.util.*;
9+
10+
public class InMemoryCacheTest {
11+
private SplitCache _cache;
12+
13+
@Before
14+
public void before() {
15+
_cache = new InMemoryCacheImp();
16+
}
17+
18+
@Test
19+
public void putAndGetSplit() {
20+
ParsedSplit split = getParsedSplit("split_name");
21+
_cache.put(split);
22+
23+
ParsedSplit result = _cache.get("split_name");
24+
Assert.assertNotNull(result);
25+
Assert.assertEquals(split.changeNumber(), result.changeNumber());
26+
Assert.assertEquals(split.trafficTypeName(), result.trafficTypeName());
27+
Assert.assertEquals(split.defaultTreatment(), result.defaultTreatment());
28+
}
29+
30+
@Test
31+
public void putDuplicateSplit() {
32+
ParsedSplit split = getParsedSplit("split_name");
33+
ParsedSplit split2 = getParsedSplit("split_name");
34+
_cache.put(split);
35+
_cache.put(split2);
36+
37+
int result = _cache.getAll().size();
38+
39+
Assert.assertEquals(1, result);
40+
}
41+
42+
@Test
43+
public void getInExistentSplit() {
44+
ParsedSplit split = getParsedSplit("split_name");
45+
_cache.put(split);
46+
47+
ParsedSplit result = _cache.get("split_name_2");
48+
Assert.assertNull(result);
49+
}
50+
51+
@Test
52+
public void removeSplit() {
53+
ParsedSplit split = getParsedSplit("split_name");
54+
ParsedSplit split2 = getParsedSplit("split_name_2");
55+
_cache.put(split);
56+
_cache.put(split2);
57+
58+
int result = _cache.getAll().size();
59+
Assert.assertEquals(2, result);
60+
61+
_cache.remove("split_name");
62+
result = _cache.getAll().size();
63+
Assert.assertEquals(1, result);
64+
65+
Assert.assertNull(_cache.get("split_name"));
66+
}
67+
68+
@Test
69+
public void setAndGetChangeNumber() {
70+
_cache.setChangeNumber(223);
71+
72+
long changeNumber = _cache.getChangeNumber();
73+
Assert.assertEquals(223, changeNumber);
74+
75+
_cache.setChangeNumber(539);
76+
changeNumber = _cache.getChangeNumber();
77+
Assert.assertEquals(539, changeNumber);
78+
}
79+
80+
@Test
81+
public void getMany() {
82+
_cache.put(getParsedSplit("split_name_1"));
83+
_cache.put(getParsedSplit("split_name_2"));
84+
_cache.put(getParsedSplit("split_name_3"));
85+
_cache.put(getParsedSplit("split_name_4"));
86+
87+
List<String> names = new ArrayList<>();
88+
names.add("split_name_2");
89+
names.add("split_name_3");
90+
91+
Collection<ParsedSplit> result = _cache.getMany(names);
92+
Assert.assertEquals(2, result.size());
93+
}
94+
95+
private ParsedSplit getParsedSplit(String splitName) {
96+
return ParsedSplit.createParsedSplitForTests(splitName, 0, false, "default_treatment", new ArrayList<>(), "tt", 123, 2);
97+
}
98+
}

0 commit comments

Comments
 (0)