-
Notifications
You must be signed in to change notification settings - Fork 21
Added inmemory storage classes #540
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
df6bc39
Added inmemory storage classes
chillaq f0d8819
Merge branch 'feature/rule-based-segment' into rbs-memory-storage
chillaq 901761a
polish
chillaq 062774c
Merge branch 'rbs-memory-storage' of https://github.com/splitio/java-…
chillaq cc9aaa1
Polish
chillaq File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
125 changes: 125 additions & 0 deletions
125
client/src/main/java/io/split/engine/experiments/ParsedRuleBasedSegment.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
package io.split.engine.experiments; | ||
|
||
import com.google.common.collect.ImmutableList; | ||
import io.split.engine.matchers.AttributeMatcher; | ||
import io.split.engine.matchers.UserDefinedSegmentMatcher; | ||
|
||
import java.util.List; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
|
||
public class ParsedRuleBasedSegment { | ||
|
||
private final String _ruleBasedSegment; | ||
private final ImmutableList<ParsedCondition> _parsedCondition; | ||
private final String _trafficTypeName; | ||
private final long _changeNumber; | ||
private final List<String> _excludedKeys; | ||
private final List<String> _excludedSegments; | ||
|
||
public static ParsedRuleBasedSegment createParsedRuleBasedSegmentForTests( | ||
String ruleBasedSegment, | ||
List<ParsedCondition> matcherAndSplits, | ||
String trafficTypeName, | ||
long changeNumber, | ||
List<String> excludedKeys, | ||
List<String> excludedSegments | ||
) { | ||
return new ParsedRuleBasedSegment( | ||
ruleBasedSegment, | ||
matcherAndSplits, | ||
trafficTypeName, | ||
changeNumber, | ||
excludedKeys, | ||
excludedSegments | ||
); | ||
} | ||
|
||
public ParsedRuleBasedSegment( | ||
String ruleBasedSegment, | ||
List<ParsedCondition> matcherAndSplits, | ||
String trafficTypeName, | ||
long changeNumber, | ||
List<String> excludedKeys, | ||
List<String> excludedSegments | ||
) { | ||
_ruleBasedSegment = ruleBasedSegment; | ||
_parsedCondition = ImmutableList.copyOf(matcherAndSplits); | ||
_trafficTypeName = trafficTypeName; | ||
_changeNumber = changeNumber; | ||
_excludedKeys = excludedKeys; | ||
_excludedSegments = excludedSegments; | ||
} | ||
|
||
public String ruleBasedSegment() { | ||
return _ruleBasedSegment; | ||
} | ||
|
||
public List<ParsedCondition> parsedConditions() { | ||
return _parsedCondition; | ||
} | ||
|
||
public String trafficTypeName() {return _trafficTypeName;} | ||
|
||
public long changeNumber() {return _changeNumber;} | ||
|
||
public List<String> excludedKeys() {return _excludedKeys;} | ||
public List<String> excludedSegments() {return _excludedSegments;} | ||
|
||
@Override | ||
public int hashCode() { | ||
int result = 17; | ||
result = 31 * result + _ruleBasedSegment.hashCode(); | ||
result = 31 * result + _parsedCondition.hashCode(); | ||
result = 31 * result + (_trafficTypeName == null ? 0 : _trafficTypeName.hashCode()); | ||
result = 31 * result + (int)(_changeNumber ^ (_changeNumber >>> 32)); | ||
return result; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object obj) { | ||
if (obj == null) return false; | ||
if (this == obj) return true; | ||
if (!(obj instanceof ParsedRuleBasedSegment)) return false; | ||
|
||
ParsedRuleBasedSegment other = (ParsedRuleBasedSegment) obj; | ||
|
||
return _ruleBasedSegment.equals(other._ruleBasedSegment) | ||
&& _parsedCondition.equals(other._parsedCondition) | ||
&& _trafficTypeName == null ? other._trafficTypeName == null : _trafficTypeName.equals(other._trafficTypeName) | ||
&& _changeNumber == other._changeNumber; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
StringBuilder bldr = new StringBuilder(); | ||
bldr.append("name:"); | ||
bldr.append(_ruleBasedSegment); | ||
bldr.append(", parsedConditions:"); | ||
bldr.append(_parsedCondition); | ||
bldr.append(", trafficTypeName:"); | ||
bldr.append(_trafficTypeName); | ||
bldr.append(", changeNumber:"); | ||
bldr.append(_changeNumber); | ||
return bldr.toString(); | ||
|
||
} | ||
|
||
public Set<String> getSegmentsNames() { | ||
return parsedConditions().stream() | ||
.flatMap(parsedCondition -> parsedCondition.matcher().attributeMatchers().stream()) | ||
.filter(ParsedRuleBasedSegment::isSegmentMatcher) | ||
.map(ParsedRuleBasedSegment::asSegmentMatcherForEach) | ||
.map(UserDefinedSegmentMatcher::getSegmentName) | ||
.collect(Collectors.toSet()); | ||
} | ||
|
||
private static boolean isSegmentMatcher(AttributeMatcher attributeMatcher) { | ||
return ((AttributeMatcher.NegatableMatcher) attributeMatcher.matcher()).delegate() instanceof UserDefinedSegmentMatcher; | ||
} | ||
|
||
private static UserDefinedSegmentMatcher asSegmentMatcherForEach(AttributeMatcher attributeMatcher) { | ||
return (UserDefinedSegmentMatcher) ((AttributeMatcher.NegatableMatcher) attributeMatcher.matcher()).delegate(); | ||
} | ||
|
||
} |
4 changes: 4 additions & 0 deletions
4
client/src/main/java/io/split/storages/RuleBasedSegmentCache.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
package io.split.storages; | ||
|
||
public interface RuleBasedSegmentCache extends RuleBasedSegmentCacheConsumer, RuleBasedSegmentCacheProducer { | ||
} |
16 changes: 16 additions & 0 deletions
16
client/src/main/java/io/split/storages/RuleBasedSegmentCacheConsumer.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package io.split.storages; | ||
|
||
import io.split.engine.experiments.ParsedRuleBasedSegment; | ||
|
||
import java.util.Collection; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
|
||
public interface RuleBasedSegmentCacheConsumer { | ||
ParsedRuleBasedSegment get(String name); | ||
Collection<ParsedRuleBasedSegment> getAll(); | ||
List<String> ruleBasedSegmentNames(); | ||
long getChangeNumber(); | ||
Set<String> getSegments(); | ||
} |
11 changes: 11 additions & 0 deletions
11
client/src/main/java/io/split/storages/RuleBasedSegmentCacheProducer.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package io.split.storages; | ||
|
||
import io.split.engine.experiments.ParsedRuleBasedSegment; | ||
|
||
import java.util.List; | ||
|
||
public interface RuleBasedSegmentCacheProducer { | ||
boolean remove(String name); | ||
void setChangeNumber(long changeNumber); | ||
void update(List<ParsedRuleBasedSegment> toAdd, List<String> toRemove, long changeNumber); | ||
} |
101 changes: 101 additions & 0 deletions
101
client/src/main/java/io/split/storages/memory/RuleBasedSegmentCacheInMemoryImp.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package io.split.storages.memory; | ||
|
||
import com.google.common.collect.Maps; | ||
import io.split.engine.experiments.ParsedRuleBasedSegment; | ||
import io.split.storages.RuleBasedSegmentCache; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.util.Collection; | ||
import java.util.List; | ||
import java.util.Set; | ||
import java.util.ArrayList; | ||
import java.util.concurrent.ConcurrentMap; | ||
import java.util.concurrent.atomic.AtomicLong; | ||
import java.util.stream.Collectors; | ||
|
||
public class RuleBasedSegmentCacheInMemoryImp implements RuleBasedSegmentCache { | ||
|
||
private static final Logger _log = LoggerFactory.getLogger(RuleBasedSegmentCacheInMemoryImp.class); | ||
|
||
private final ConcurrentMap<String, ParsedRuleBasedSegment> _concurrentMap; | ||
|
||
private AtomicLong _changeNumber; | ||
|
||
public RuleBasedSegmentCacheInMemoryImp() { | ||
this(-1); | ||
} | ||
|
||
public RuleBasedSegmentCacheInMemoryImp(long startingChangeNumber) { | ||
_concurrentMap = Maps.newConcurrentMap(); | ||
_changeNumber = new AtomicLong(startingChangeNumber); | ||
} | ||
|
||
@Override | ||
public boolean remove(String name) { | ||
ParsedRuleBasedSegment removed = _concurrentMap.remove(name); | ||
return removed != null; | ||
} | ||
|
||
@Override | ||
public ParsedRuleBasedSegment get(String name) { | ||
return _concurrentMap.get(name); | ||
} | ||
|
||
@Override | ||
public Collection<ParsedRuleBasedSegment> getAll() { | ||
return _concurrentMap.values(); | ||
} | ||
|
||
@Override | ||
public long getChangeNumber() { | ||
return _changeNumber.get(); | ||
} | ||
|
||
@Override | ||
public void setChangeNumber(long changeNumber) { | ||
if (changeNumber < _changeNumber.get()) { | ||
_log.error("ChangeNumber for feature flags cache is less than previous"); | ||
} | ||
|
||
_changeNumber.set(changeNumber); | ||
} | ||
|
||
@Override | ||
public List<String> ruleBasedSegmentNames() { | ||
List<String> ruleBasedSegmentNamesList = new ArrayList<>(); | ||
for (String key: _concurrentMap.keySet()) { | ||
ruleBasedSegmentNamesList.add(_concurrentMap.get(key).ruleBasedSegment()); | ||
} | ||
return ruleBasedSegmentNamesList; | ||
} | ||
|
||
public void clear() { | ||
_concurrentMap.clear(); | ||
} | ||
|
||
private void putMany(List<ParsedRuleBasedSegment> ruleBasedSegments) { | ||
for (ParsedRuleBasedSegment ruleBasedSegment : ruleBasedSegments) { | ||
_concurrentMap.put(ruleBasedSegment.ruleBasedSegment(), ruleBasedSegment); | ||
} | ||
} | ||
|
||
@Override | ||
public void update(List<ParsedRuleBasedSegment> toAdd, List<String> toRemove, long changeNumber) { | ||
if(toAdd != null) { | ||
putMany(toAdd); | ||
} | ||
if(toRemove != null) { | ||
for(String ruleBasedSegment : toRemove) { | ||
remove(ruleBasedSegment); | ||
} | ||
} | ||
setChangeNumber(changeNumber); | ||
} | ||
|
||
public Set<String> getSegments() { | ||
return _concurrentMap.values().stream() | ||
.flatMap(parsedRuleBasedSegment -> parsedRuleBasedSegment.getSegmentsNames().stream()).collect(Collectors.toSet()); | ||
} | ||
} |
30 changes: 30 additions & 0 deletions
30
client/src/test/java/io/split/engine/experiments/ParsedRuleBasedSegmentTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package io.split.engine.experiments; | ||
|
||
import com.google.common.collect.Lists; | ||
import com.google.common.collect.Sets; | ||
import io.split.client.dtos.ConditionType; | ||
import io.split.client.dtos.MatcherCombiner; | ||
import io.split.engine.matchers.AttributeMatcher; | ||
import io.split.engine.matchers.CombiningMatcher; | ||
import io.split.engine.matchers.UserDefinedSegmentMatcher; | ||
|
||
import org.junit.Assert; | ||
import org.junit.Test; | ||
|
||
public class ParsedRuleBasedSegmentTest { | ||
|
||
@Test | ||
public void works() { | ||
AttributeMatcher segmentMatcher = AttributeMatcher.vanilla(new UserDefinedSegmentMatcher("employees")); | ||
CombiningMatcher segmentCombiningMatcher = new CombiningMatcher(MatcherCombiner.AND, Lists.newArrayList(segmentMatcher)); | ||
ParsedRuleBasedSegment parsedRuleBasedSegment = new ParsedRuleBasedSegment("another_rule_based_segment", | ||
Lists.newArrayList(new ParsedCondition(ConditionType.WHITELIST, segmentCombiningMatcher, null, "label")),"user", | ||
123, Lists.newArrayList("mauro@test.io","gaston@test.io"), Lists.newArrayList("segment1", "segment2")); | ||
|
||
Assert.assertEquals(Sets.newHashSet("employees"), parsedRuleBasedSegment.getSegmentsNames()); | ||
Assert.assertEquals("another_rule_based_segment", parsedRuleBasedSegment.ruleBasedSegment()); | ||
Assert.assertEquals(Lists.newArrayList(new ParsedCondition(ConditionType.WHITELIST, segmentCombiningMatcher, null, "label")), | ||
parsedRuleBasedSegment.parsedConditions()); | ||
Assert.assertEquals(123, parsedRuleBasedSegment.changeNumber()); | ||
} | ||
} |
55 changes: 55 additions & 0 deletions
55
client/src/test/java/io/split/storages/memory/RuleBasedSegmentCacheInMemoryImplTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package io.split.storages.memory; | ||
|
||
import com.google.common.collect.Sets; | ||
import io.split.client.dtos.MatcherCombiner; | ||
import io.split.engine.experiments.ParsedRuleBasedSegment; | ||
import io.split.engine.experiments.ParsedCondition; | ||
import io.split.client.dtos.ConditionType; | ||
|
||
import io.split.engine.matchers.AttributeMatcher; | ||
import io.split.engine.matchers.CombiningMatcher; | ||
import io.split.engine.matchers.UserDefinedSegmentMatcher; | ||
import io.split.engine.matchers.strings.WhitelistMatcher; | ||
import junit.framework.TestCase; | ||
import org.junit.Test; | ||
import com.google.common.collect.Lists; | ||
|
||
public class RuleBasedSegmentCacheInMemoryImplTest extends TestCase { | ||
|
||
@Test | ||
public void testAddAndDeleteSegment(){ | ||
RuleBasedSegmentCacheInMemoryImp ruleBasedSegmentCache = new RuleBasedSegmentCacheInMemoryImp(); | ||
AttributeMatcher whiteListMatcher = AttributeMatcher.vanilla(new WhitelistMatcher(Lists.newArrayList("test_1", "admin"))); | ||
CombiningMatcher whitelistCombiningMatcher = new CombiningMatcher(MatcherCombiner.AND, Lists.newArrayList(whiteListMatcher)); | ||
ParsedRuleBasedSegment parsedRuleBasedSegment = new ParsedRuleBasedSegment("sample_rule_based_segment", | ||
Lists.newArrayList(new ParsedCondition(ConditionType.WHITELIST, whitelistCombiningMatcher, null, "label")),"user", | ||
123, Lists.newArrayList("mauro@test.io","gaston@test.io"), Lists.newArrayList()); | ||
ruleBasedSegmentCache.update(Lists.newArrayList(parsedRuleBasedSegment), null, 123); | ||
assertEquals(123, ruleBasedSegmentCache.getChangeNumber()); | ||
assertEquals(parsedRuleBasedSegment, ruleBasedSegmentCache.get("sample_rule_based_segment")); | ||
|
||
ruleBasedSegmentCache.update(null, Lists.newArrayList("sample_rule_based_segment"), 124); | ||
assertEquals(124, ruleBasedSegmentCache.getChangeNumber()); | ||
assertEquals(null, ruleBasedSegmentCache.get("sample_rule_based_segment")); | ||
} | ||
|
||
@Test | ||
public void testMultipleSegment(){ | ||
RuleBasedSegmentCacheInMemoryImp ruleBasedSegmentCache = new RuleBasedSegmentCacheInMemoryImp(); | ||
AttributeMatcher whiteListMatcher = AttributeMatcher.vanilla(new WhitelistMatcher(Lists.newArrayList("test_1", "admin"))); | ||
CombiningMatcher whitelistCombiningMatcher = new CombiningMatcher(MatcherCombiner.AND, Lists.newArrayList(whiteListMatcher)); | ||
ParsedRuleBasedSegment parsedRuleBasedSegment1 = new ParsedRuleBasedSegment("sample_rule_based_segment", | ||
Lists.newArrayList(new ParsedCondition(ConditionType.WHITELIST, whitelistCombiningMatcher, null, "label")),"user", | ||
123, Lists.newArrayList("mauro@test.io","gaston@test.io"), Lists.newArrayList(Lists.newArrayList("segment1", "segment3"))); | ||
|
||
AttributeMatcher segmentMatcher = AttributeMatcher.vanilla(new UserDefinedSegmentMatcher("employees")); | ||
CombiningMatcher segmentCombiningMatcher = new CombiningMatcher(MatcherCombiner.AND, Lists.newArrayList(segmentMatcher)); | ||
ParsedRuleBasedSegment parsedRuleBasedSegment2 = new ParsedRuleBasedSegment("another_rule_based_segment", | ||
Lists.newArrayList(new ParsedCondition(ConditionType.WHITELIST, segmentCombiningMatcher, null, "label")),"user", | ||
123, Lists.newArrayList("mauro@test.io","gaston@test.io"), Lists.newArrayList("segment1", "segment2")); | ||
|
||
ruleBasedSegmentCache.update(Lists.newArrayList(parsedRuleBasedSegment1, parsedRuleBasedSegment2), null, 123); | ||
assertEquals(Lists.newArrayList("another_rule_based_segment", "sample_rule_based_segment"), ruleBasedSegmentCache.ruleBasedSegmentNames()); | ||
assertEquals(Sets.newHashSet("employees"), ruleBasedSegmentCache.getSegments()); | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.