Skip to content

Updated Evaluator #541

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 4 commits into from
Apr 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions client/src/main/java/io/split/client/SplitFactoryImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,12 @@
import io.split.storages.SplitCache;
import io.split.storages.SplitCacheConsumer;
import io.split.storages.SplitCacheProducer;
import io.split.storages.RuleBasedSegmentCacheConsumer;
import io.split.storages.RuleBasedSegmentCache;
import io.split.storages.enums.OperationMode;
import io.split.storages.memory.InMemoryCacheImp;
import io.split.storages.memory.SegmentCacheInMemoryImpl;
import io.split.storages.memory.RuleBasedSegmentCacheInMemoryImp;
import io.split.storages.pluggable.adapters.UserCustomEventAdapterProducer;
import io.split.storages.pluggable.adapters.UserCustomImpressionAdapterConsumer;
import io.split.storages.pluggable.adapters.UserCustomImpressionAdapterProducer;
Expand Down Expand Up @@ -202,6 +205,7 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn

// Cache Initialisations
SegmentCache segmentCache = new SegmentCacheInMemoryImpl();
RuleBasedSegmentCache ruleBasedSegmentCache = new RuleBasedSegmentCacheInMemoryImp();
FlagSetsFilter flagSetsFilter = new FlagSetsFilterImpl(config.getSetsFilter());
SplitCache splitCache = new InMemoryCacheImp(flagSetsFilter);
ImpressionsStorage impressionsStorage = new InMemoryImpressionsStorage(config.impressionsQueueSize());
Expand Down Expand Up @@ -244,7 +248,7 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
config.getThreadFactory());

// Evaluator
_evaluator = new EvaluatorImp(splitCache, segmentCache);
_evaluator = new EvaluatorImp(splitCache, segmentCache, ruleBasedSegmentCache);

// SplitClient
_client = new SplitClientImpl(this,
Expand Down Expand Up @@ -333,7 +337,9 @@ protected SplitFactoryImpl(String apiToken, SplitClientConfig config, CustomStor
_gates = new SDKReadinessGates();

_telemetrySynchronizer = new TelemetryConsumerSubmitter(customStorageWrapper, _sdkMetadata);
_evaluator = new EvaluatorImp(userCustomSplitAdapterConsumer, userCustomSegmentAdapterConsumer);
// TODO Update the instance to UserCustomRuleBasedSegmentAdapterConsumer
RuleBasedSegmentCacheConsumer userCustomRuleBasedSegmentAdapterConsumer = new RuleBasedSegmentCacheInMemoryImp();
_evaluator = new EvaluatorImp(userCustomSplitAdapterConsumer, userCustomSegmentAdapterConsumer, userCustomRuleBasedSegmentAdapterConsumer);
_impressionsSender = PluggableImpressionSender.create(customStorageWrapper);
_uniqueKeysTracker = createUniqueKeysTracker(config);
_impressionsManager = buildImpressionsManager(config, userCustomImpressionAdapterConsumer,
Expand Down Expand Up @@ -392,6 +398,7 @@ protected SplitFactoryImpl(SplitClientConfig config) {
SegmentCache segmentCache = new SegmentCacheInMemoryImpl();
FlagSetsFilter flagSetsFilter = new FlagSetsFilterImpl(config.getSetsFilter());
SplitCache splitCache = new InMemoryCacheImp(flagSetsFilter);
RuleBasedSegmentCache _ruleBasedSegmentCache = new RuleBasedSegmentCacheInMemoryImp();
_splitCache = splitCache;
_gates = new SDKReadinessGates();
_segmentCache = segmentCache;
Expand Down Expand Up @@ -428,7 +435,7 @@ protected SplitFactoryImpl(SplitClientConfig config) {
_impressionsManager, null, null, null);

// Evaluator
_evaluator = new EvaluatorImp(splitCache, segmentCache);
_evaluator = new EvaluatorImp(splitCache, segmentCache, _ruleBasedSegmentCache);

EventsStorage eventsStorage = new NoopEventsStorageImp();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
package io.split.engine.evaluator;

import io.split.storages.RuleBasedSegmentCacheConsumer;
import io.split.storages.SegmentCacheConsumer;

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

public class EvaluationContext {
private final Evaluator _evaluator;
private final SegmentCacheConsumer _segmentCacheConsumer;
private final RuleBasedSegmentCacheConsumer _ruleBasedSegmentCacheConsumer;

public EvaluationContext(Evaluator evaluator, SegmentCacheConsumer segmentCacheConsumer) {
public EvaluationContext(Evaluator evaluator, SegmentCacheConsumer segmentCacheConsumer,
RuleBasedSegmentCacheConsumer ruleBasedSegmentCacheConsumer) {
_evaluator = checkNotNull(evaluator);
_segmentCacheConsumer = checkNotNull(segmentCacheConsumer);
_ruleBasedSegmentCacheConsumer = checkNotNull(ruleBasedSegmentCacheConsumer);
}

public Evaluator getEvaluator() {
Expand All @@ -20,4 +24,8 @@ public Evaluator getEvaluator() {
public SegmentCacheConsumer getSegmentCache() {
return _segmentCacheConsumer;
}

public RuleBasedSegmentCacheConsumer getRuleBasedSegmentCache() {
return _ruleBasedSegmentCacheConsumer;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import io.split.engine.experiments.ParsedSplit;
import io.split.engine.splitter.Splitter;
import io.split.grammar.Treatments;
import io.split.storages.RuleBasedSegmentCacheConsumer;
import io.split.storages.SegmentCacheConsumer;
import io.split.storages.SplitCacheConsumer;
import org.slf4j.Logger;
Expand All @@ -23,13 +24,16 @@ public class EvaluatorImp implements Evaluator {
private static final Logger _log = LoggerFactory.getLogger(EvaluatorImp.class);

private final SegmentCacheConsumer _segmentCacheConsumer;
private final RuleBasedSegmentCacheConsumer _ruleBasedSegmentCacheConsumer;
private final EvaluationContext _evaluationContext;
private final SplitCacheConsumer _splitCacheConsumer;

public EvaluatorImp(SplitCacheConsumer splitCacheConsumer, SegmentCacheConsumer segmentCache) {
public EvaluatorImp(SplitCacheConsumer splitCacheConsumer, SegmentCacheConsumer segmentCache,
RuleBasedSegmentCacheConsumer ruleBasedSegmentCacheConsumer) {
_splitCacheConsumer = checkNotNull(splitCacheConsumer);
_segmentCacheConsumer = checkNotNull(segmentCache);
_evaluationContext = new EvaluationContext(this, _segmentCacheConsumer);
_ruleBasedSegmentCacheConsumer = checkNotNull(ruleBasedSegmentCacheConsumer);
_evaluationContext = new EvaluationContext(this, _segmentCacheConsumer, ruleBasedSegmentCacheConsumer);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package io.split.engine.matchers;

import io.split.engine.evaluator.EvaluationContext;
import io.split.engine.experiments.ParsedCondition;
import io.split.engine.experiments.ParsedRuleBasedSegment;

import java.util.List;
import java.util.Map;

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

/**
* A matcher that checks if the key is part of a user defined segment. This class
* assumes that the logic for refreshing what keys are part of a segment is delegated
* to SegmentFetcher.
*
* @author adil
*/
public class RuleBasedSegmentMatcher implements Matcher {
private final String _segmentName;

public RuleBasedSegmentMatcher(String segmentName) {
_segmentName = checkNotNull(segmentName);
}

@Override
public boolean match(Object matchValue, String bucketingKey, Map<String, Object> attributes, EvaluationContext evaluationContext) {
if (!(matchValue instanceof String)) {
return false;
}
ParsedRuleBasedSegment parsedRuleBasedSegment = evaluationContext.getRuleBasedSegmentCache().get(_segmentName);
if (parsedRuleBasedSegment == null) {
return false;
}

if (parsedRuleBasedSegment.excludedKeys().contains(matchValue)) {
return false;
}

for (String segmentName: parsedRuleBasedSegment.excludedSegments()) {
if (evaluationContext.getSegmentCache().isInSegment(segmentName, (String) matchValue)) {
return false;
}
}
List<ParsedCondition> conditions = parsedRuleBasedSegment.parsedConditions();
for (ParsedCondition parsedCondition : conditions) {
if (parsedCondition.matcher().match((String) matchValue, bucketingKey, attributes, evaluationContext)) {
return true;
}
}
return false;
}

@Override
public int hashCode() {
int result = 17;
result = 31 * result + _segmentName.hashCode();
return result;
}

@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (this == obj) return true;
if (!(obj instanceof RuleBasedSegmentMatcher)) return false;

RuleBasedSegmentMatcher other = (RuleBasedSegmentMatcher) obj;

return _segmentName.equals(other._segmentName);
}

@Override
public String toString() {
StringBuilder bldr = new StringBuilder();
bldr.append("in segment ");
bldr.append(_segmentName);
return bldr.toString();
}

public String getSegmentName() {
return _segmentName;
}
}
Loading