Skip to content
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
88 changes: 47 additions & 41 deletions client/src/main/java/io/split/client/SplitClientImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
import com.google.gson.GsonBuilder;
import io.split.client.api.Key;
import io.split.client.api.SplitResult;
import io.split.client.dtos.DecoratedImpression;
import io.split.client.dtos.EvaluationOptions;
import io.split.client.dtos.Event;
import io.split.client.dtos.*;
import io.split.client.events.EventsStorageProducer;
import io.split.client.impressions.Impression;
import io.split.client.impressions.ImpressionsManager;
Expand All @@ -14,7 +12,6 @@
import io.split.engine.evaluator.Evaluator;
import io.split.engine.evaluator.EvaluatorImp;
import io.split.engine.evaluator.Labels;
import io.split.grammar.Treatments;
import io.split.inputValidation.EventsValidator;
import io.split.inputValidation.KeyValidator;
import io.split.inputValidation.SplitNameValidator;
Expand Down Expand Up @@ -49,7 +46,6 @@
* @author adil
*/
public final class SplitClientImpl implements SplitClient {
public static final SplitResult SPLIT_RESULT_CONTROL = new SplitResult(Treatments.CONTROL, null);
private static final String CLIENT_DESTROY = "Client has already been destroyed - no calls possible";
private static final String CATCHALL_EXCEPTION = "CatchAll Exception";
private static final String MATCHING_KEY = "matchingKey";
Expand All @@ -66,6 +62,7 @@ public final class SplitClientImpl implements SplitClient {
private final TelemetryEvaluationProducer _telemetryEvaluationProducer;
private final TelemetryConfigProducer _telemetryConfigProducer;
private final FlagSetsFilter _flagSetsFilter;
private final FallbackTreatmentCalculator _fallbackTreatmentCalculator;

public SplitClientImpl(SplitFactory container,
SplitCacheConsumer splitCacheConsumer,
Expand All @@ -76,7 +73,8 @@ public SplitClientImpl(SplitFactory container,
Evaluator evaluator,
TelemetryEvaluationProducer telemetryEvaluationProducer,
TelemetryConfigProducer telemetryConfigProducer,
FlagSetsFilter flagSetsFilter) {
FlagSetsFilter flagSetsFilter,
FallbackTreatmentCalculator fallbackTreatmentCalculator) {
_container = container;
_splitCacheConsumer = checkNotNull(splitCacheConsumer);
_impressionManager = checkNotNull(impressionManager);
Expand All @@ -87,6 +85,7 @@ public SplitClientImpl(SplitFactory container,
_telemetryEvaluationProducer = checkNotNull(telemetryEvaluationProducer);
_telemetryConfigProducer = checkNotNull(telemetryConfigProducer);
_flagSetsFilter = flagSetsFilter;
_fallbackTreatmentCalculator = fallbackTreatmentCalculator;
}

@Override
Expand Down Expand Up @@ -492,31 +491,31 @@ private SplitResult getTreatmentWithConfigInternal(String matchingKey, String bu

if (_container.isDestroyed()) {
_log.error(CLIENT_DESTROY);
return SPLIT_RESULT_CONTROL;
return checkFallbackTreatment(featureFlag);
}

if (!KeyValidator.isValid(matchingKey, MATCHING_KEY, _config.maxStringLength(), methodEnum.getMethod())) {
return SPLIT_RESULT_CONTROL;
return checkFallbackTreatment(featureFlag);
}

if (!KeyValidator.bucketingKeyIsValid(bucketingKey, _config.maxStringLength(), methodEnum.getMethod())) {
return SPLIT_RESULT_CONTROL;
return checkFallbackTreatment(featureFlag);
}

Optional<String> splitNameResult = SplitNameValidator.isValid(featureFlag, methodEnum.getMethod());
if (!splitNameResult.isPresent()) {
return SPLIT_RESULT_CONTROL;
return checkFallbackTreatment(featureFlag);
}
featureFlag = splitNameResult.get();
long start = System.currentTimeMillis();

EvaluatorImp.TreatmentLabelAndChangeNumber result = _evaluator.evaluateFeature(matchingKey, bucketingKey, featureFlag, attributes);

if (result.treatment.equals(Treatments.CONTROL) && result.label.equals(Labels.DEFINITION_NOT_FOUND) && _gates.isSDKReady()) {
if (result.label != null && result.label.contains(Labels.DEFINITION_NOT_FOUND) && _gates.isSDKReady()) {
_log.warn(String.format(
"%s: you passed \"%s\" that does not exist in this environment, " +
"please double check what feature flags exist in the Split user interface.", methodEnum.getMethod(), featureFlag));
return SPLIT_RESULT_CONTROL;
return checkFallbackTreatment(featureFlag);
}

recordStats(
Expand All @@ -541,10 +540,19 @@ private SplitResult getTreatmentWithConfigInternal(String matchingKey, String bu
} catch (Exception e1) {
// ignore
}
return SPLIT_RESULT_CONTROL;
return checkFallbackTreatment(featureFlag);
}
}

private SplitResult checkFallbackTreatment(String featureName) {
FallbackTreatment fallbackTreatment = _fallbackTreatmentCalculator.resolve(featureName, "");
String config = null;
if (fallbackTreatment.getConfig() != null) {
config = fallbackTreatment.getConfig();
}
return new SplitResult(fallbackTreatment.getTreatment(), config);
}

private String validateProperties(Map<String, Object> properties) {
if (properties == null){
return null;
Expand All @@ -563,6 +571,7 @@ private Map<String, SplitResult> getTreatmentsWithConfigInternal(String matching
_log.error(String.format("%s: featureFlagNames must be a non-empty array", methodEnum.getMethod()));
return new HashMap<>();
}

try {
checkSDKReady(methodEnum, featureFlagNames);
Map<String, SplitResult> result = validateBeforeEvaluate(featureFlagNames, matchingKey, methodEnum, bucketingKey);
Expand Down Expand Up @@ -601,46 +610,43 @@ private Map<String, SplitResult> getTreatmentsBySetsWithConfigInternal(String ma
if (cleanFlagSets.isEmpty()) {
return new HashMap<>();
}
List<String> featureFlagNames = new ArrayList<>();
try {
checkSDKReady(methodEnum);
Map<String, SplitResult> result = validateBeforeEvaluateByFlagSets(matchingKey, methodEnum,bucketingKey);
if(result != null) {
return result;
}
Map<String, EvaluatorImp.TreatmentLabelAndChangeNumber> evaluatorResult = _evaluator.evaluateFeaturesByFlagSets(matchingKey,
bucketingKey, new ArrayList<>(cleanFlagSets), attributes);
checkSDKReady(methodEnum);
Map<String, SplitResult> result = validateBeforeEvaluateByFlagSets(matchingKey, methodEnum,bucketingKey);
if(result != null) {
return result;
}
Map<String, EvaluatorImp.TreatmentLabelAndChangeNumber> evaluatorResult = _evaluator.evaluateFeaturesByFlagSets(matchingKey,
bucketingKey, new ArrayList<>(cleanFlagSets), attributes);

return processEvaluatorResult(evaluatorResult, methodEnum, matchingKey, bucketingKey, attributes, initTime,
validateProperties(evaluationOptions.getProperties()));
} catch (Exception e) {
try {
evaluatorResult.entrySet().forEach(flag -> {
if (flag.getValue().label != null &&
flag.getValue().label.contains(io.split.engine.evaluator.Labels.EXCEPTION)) {
_telemetryEvaluationProducer.recordException(methodEnum);
_log.error(CATCHALL_EXCEPTION, e);
} catch (Exception e1) {
// ignore
}
return createMapControl(featureFlagNames);
}
});
return processEvaluatorResult(evaluatorResult, methodEnum, matchingKey, bucketingKey, attributes, initTime,
validateProperties(evaluationOptions.getProperties()));
}

private Map<String, SplitResult> processEvaluatorResult(Map<String, EvaluatorImp.TreatmentLabelAndChangeNumber> evaluatorResult,
MethodEnum methodEnum, String matchingKey, String bucketingKey, Map<String,
Object> attributes, long initTime, String properties){
List<DecoratedImpression> decoratedImpressions = new ArrayList<>();
Map<String, SplitResult> result = new HashMap<>();
evaluatorResult.keySet().forEach(t -> {
if (evaluatorResult.get(t).treatment.equals(Treatments.CONTROL) && evaluatorResult.get(t).label.
equals(Labels.DEFINITION_NOT_FOUND) && _gates.isSDKReady()) {
evaluatorResult.keySet().forEach(flag -> {
if (evaluatorResult.get(flag).label != null &&
evaluatorResult.get(flag).label.contains(Labels.DEFINITION_NOT_FOUND) &&
_gates.isSDKReady()) {
_log.warn(String.format("%s: you passed \"%s\" that does not exist in this environment please double check " +
"what feature flags exist in the Split user interface.", methodEnum.getMethod(), t));
result.put(t, SPLIT_RESULT_CONTROL);
"what feature flags exist in the Split user interface.", methodEnum.getMethod(), flag));
result.put(flag, checkFallbackTreatment(flag));
} else {
result.put(t, new SplitResult(evaluatorResult.get(t).treatment, evaluatorResult.get(t).configurations));
result.put(flag, new SplitResult(evaluatorResult.get(flag).treatment, evaluatorResult.get(flag).configurations));
decoratedImpressions.add(
new DecoratedImpression(
new Impression(matchingKey, bucketingKey, t, evaluatorResult.get(t).treatment, System.currentTimeMillis(),
evaluatorResult.get(t).label, evaluatorResult.get(t).changeNumber, attributes, properties),
evaluatorResult.get(t).track));
new Impression(matchingKey, bucketingKey, flag, evaluatorResult.get(flag).treatment, System.currentTimeMillis(),
evaluatorResult.get(flag).label, evaluatorResult.get(flag).changeNumber, attributes, properties),
evaluatorResult.get(flag).track));
}
});
_telemetryEvaluationProducer.recordLatency(methodEnum, System.currentTimeMillis() - initTime);
Expand Down Expand Up @@ -735,7 +741,7 @@ private void checkSDKReady(MethodEnum methodEnum) {

private Map<String, SplitResult> createMapControl(List<String> featureFlags) {
Map<String, SplitResult> result = new HashMap<>();
featureFlags.forEach(s -> result.put(s, SPLIT_RESULT_CONTROL));
featureFlags.forEach(s -> result.put(s, checkFallbackTreatment(s)));
return result;
}
}
22 changes: 16 additions & 6 deletions client/src/main/java/io/split/client/SplitFactoryImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.google.common.io.Files;
import io.split.client.dtos.BearerCredentialsProvider;
import io.split.client.dtos.FallbackTreatmentCalculatorImp;
import io.split.client.dtos.Metadata;
import io.split.client.events.EventsSender;
import io.split.client.events.EventsStorage;
Expand Down Expand Up @@ -256,8 +257,9 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
_telemetrySyncTask = new TelemetrySyncTask(config.getTelemetryRefreshRate(), _telemetrySynchronizer,
config.getThreadFactory());

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

// SplitClient
_client = new SplitClientImpl(this,
Expand All @@ -269,7 +271,9 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
_evaluator,
_telemetryStorageProducer, // TelemetryEvaluation instance
_telemetryStorageProducer, // TelemetryConfiguration instance
flagSetsFilter);
flagSetsFilter,
fallbackTreatmentCalculatorImp
);

// SplitManager
_manager = new SplitManagerImpl(splitCache, config, _gates, _telemetryStorageProducer);
Expand Down Expand Up @@ -348,8 +352,9 @@ protected SplitFactoryImpl(String apiToken, SplitClientConfig config, CustomStor
_telemetrySynchronizer = new TelemetryConsumerSubmitter(customStorageWrapper, _sdkMetadata);
UserCustomRuleBasedSegmentAdapterConsumer userCustomRuleBasedSegmentAdapterConsumer =
new UserCustomRuleBasedSegmentAdapterConsumer(customStorageWrapper);
FallbackTreatmentCalculatorImp fallbackTreatmentCalculatorImp = new FallbackTreatmentCalculatorImp(null);
_evaluator = new EvaluatorImp(userCustomSplitAdapterConsumer, userCustomSegmentAdapterConsumer,
userCustomRuleBasedSegmentAdapterConsumer, null);
userCustomRuleBasedSegmentAdapterConsumer, fallbackTreatmentCalculatorImp);
_impressionsSender = PluggableImpressionSender.create(customStorageWrapper);
_uniqueKeysTracker = createUniqueKeysTracker(config);
_impressionsManager = buildImpressionsManager(config, userCustomImpressionAdapterConsumer,
Expand Down Expand Up @@ -378,7 +383,9 @@ protected SplitFactoryImpl(String apiToken, SplitClientConfig config, CustomStor
_evaluator,
_telemetryStorageProducer, // TelemetryEvaluation instance
_telemetryStorageProducer, // TelemetryConfiguration instance
flagSetsFilter);
flagSetsFilter,
fallbackTreatmentCalculatorImp
);

// SyncManager
_syncManager = new ConsumerSyncManager(synchronizer);
Expand Down Expand Up @@ -446,8 +453,9 @@ protected SplitFactoryImpl(SplitClientConfig config) {
SplitTasks splitTasks = SplitTasks.build(_splitSynchronizationTask, _segmentSynchronizationTaskImp,
_impressionsManager, null, null, null);

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

EventsStorage eventsStorage = new NoopEventsStorageImp();

Expand All @@ -461,7 +469,9 @@ protected SplitFactoryImpl(SplitClientConfig config) {
_evaluator,
_telemetryStorageProducer, // TelemetryEvaluation instance
_telemetryStorageProducer, // TelemetryConfiguration instance
flagSetsFilter);
flagSetsFilter,
fallbackTreatmentCalculatorImp
);

// Synchronizer
Synchronizer synchronizer = new LocalhostSynchronizer(splitTasks, _splitFetcher,
Expand Down
10 changes: 4 additions & 6 deletions client/src/main/java/io/split/client/dtos/FallbackTreatment.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package io.split.client.dtos;

import java.util.Map;

public class FallbackTreatment {
private final Map<String, Object> _config;
private final String _config;
private final String _treatment;
private final String _label;

public FallbackTreatment(String treatment, Map<String, Object> config) {
public FallbackTreatment(String treatment, String config) {
_treatment = treatment;
_config = config;
_label = null;
Expand All @@ -19,13 +17,13 @@ public FallbackTreatment(String treatment) {
_label = null;
}

public FallbackTreatment(String treatment, Map<String, Object> config, String label) {
public FallbackTreatment(String treatment, String config, String label) {
_treatment = treatment;
_config = config;
_label = label;
}

public Map<String, Object> getConfig() {
public String getConfig() {
return _config;
}

Expand Down
39 changes: 34 additions & 5 deletions client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@
import io.split.client.dtos.ConditionType;
import io.split.client.dtos.FallbackTreatment;
import io.split.client.dtos.FallbackTreatmentCalculator;
import io.split.client.dtos.FallbackTreatmentsConfiguration;
import io.split.client.exceptions.ChangeNumberExceptionWrapper;
import io.split.engine.experiments.ParsedCondition;
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;
Expand Down Expand Up @@ -63,7 +61,27 @@ public Map<String, TreatmentLabelAndChangeNumber> evaluateFeatures(String matchi
public Map<String, EvaluatorImp.TreatmentLabelAndChangeNumber> evaluateFeaturesByFlagSets(String key, String bucketingKey,
List<String> flagSets, Map<String, Object> attributes) {
List<String> flagSetsWithNames = getFeatureFlagNamesByFlagSets(flagSets);
return evaluateFeatures(key, bucketingKey, flagSetsWithNames, attributes);
try {
return evaluateFeatures(key, bucketingKey, flagSetsWithNames, attributes);
} catch (Exception e) {
_log.error("Evaluator Exception", e);
return createMapControl(flagSetsWithNames, io.split.engine.evaluator.Labels.EXCEPTION);
}
}

private Map<String, EvaluatorImp.TreatmentLabelAndChangeNumber> createMapControl(List<String> featureFlags, String label) {
Map<String, TreatmentLabelAndChangeNumber> result = new HashMap<>();
featureFlags.forEach(s -> result.put(s, checkFallbackTreatment(s, label)));
return result;
}

private EvaluatorImp.TreatmentLabelAndChangeNumber checkFallbackTreatment(String featureName, String label) {
FallbackTreatment fallbackTreatment = _fallbackTreatmentCalculator.resolve(featureName, label);
return new EvaluatorImp.TreatmentLabelAndChangeNumber(fallbackTreatment.getTreatment(),
fallbackTreatment.getLabel(),
null,
getFallbackConfig(fallbackTreatment),
false);
}

private List<String> getFeatureFlagNamesByFlagSets(List<String> flagSets) {
Expand Down Expand Up @@ -177,13 +195,24 @@ private String getConfig(ParsedSplit parsedSplit, String returnedTreatment) {
return parsedSplit.configurations() != null ? parsedSplit.configurations().get(returnedTreatment) : null;
}

private String getFallbackConfig(FallbackTreatment fallbackTreatment) {
if (fallbackTreatment.getConfig() != null) {
return fallbackTreatment.getConfig();
}

return null;
}

private TreatmentLabelAndChangeNumber evaluateParsedSplit(String matchingKey, String bucketingKey, Map<String, Object> attributes,
ParsedSplit parsedSplit, String featureName) {
try {

if (parsedSplit == null) {
FallbackTreatment fallbackTreatment = _fallbackTreatmentCalculator.resolve(featureName, Labels.DEFINITION_NOT_FOUND);
return new TreatmentLabelAndChangeNumber(fallbackTreatment.getTreatment(), fallbackTreatment.getLabel());
return new TreatmentLabelAndChangeNumber(fallbackTreatment.getTreatment(),
fallbackTreatment.getLabel(),
null,
getFallbackConfig(fallbackTreatment),
false);
}
return getTreatment(matchingKey, bucketingKey, parsedSplit, attributes);
} catch (ChangeNumberExceptionWrapper e) {
Expand Down
Loading