Skip to content

Commit 92c8b1b

Browse files
authored
Merge pull request #599 from splitio/FME-9878-fallback-client
Update client class
2 parents d0c503b + 076a850 commit 92c8b1b

File tree

7 files changed

+572
-121
lines changed

7 files changed

+572
-121
lines changed

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

Lines changed: 47 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33
import com.google.gson.GsonBuilder;
44
import io.split.client.api.Key;
55
import io.split.client.api.SplitResult;
6-
import io.split.client.dtos.DecoratedImpression;
7-
import io.split.client.dtos.EvaluationOptions;
8-
import io.split.client.dtos.Event;
6+
import io.split.client.dtos.*;
97
import io.split.client.events.EventsStorageProducer;
108
import io.split.client.impressions.Impression;
119
import io.split.client.impressions.ImpressionsManager;
@@ -14,7 +12,6 @@
1412
import io.split.engine.evaluator.Evaluator;
1513
import io.split.engine.evaluator.EvaluatorImp;
1614
import io.split.engine.evaluator.Labels;
17-
import io.split.grammar.Treatments;
1815
import io.split.inputValidation.EventsValidator;
1916
import io.split.inputValidation.KeyValidator;
2017
import io.split.inputValidation.SplitNameValidator;
@@ -49,7 +46,6 @@
4946
* @author adil
5047
*/
5148
public final class SplitClientImpl implements SplitClient {
52-
public static final SplitResult SPLIT_RESULT_CONTROL = new SplitResult(Treatments.CONTROL, null);
5349
private static final String CLIENT_DESTROY = "Client has already been destroyed - no calls possible";
5450
private static final String CATCHALL_EXCEPTION = "CatchAll Exception";
5551
private static final String MATCHING_KEY = "matchingKey";
@@ -66,6 +62,7 @@ public final class SplitClientImpl implements SplitClient {
6662
private final TelemetryEvaluationProducer _telemetryEvaluationProducer;
6763
private final TelemetryConfigProducer _telemetryConfigProducer;
6864
private final FlagSetsFilter _flagSetsFilter;
65+
private final FallbackTreatmentCalculator _fallbackTreatmentCalculator;
6966

7067
public SplitClientImpl(SplitFactory container,
7168
SplitCacheConsumer splitCacheConsumer,
@@ -76,7 +73,8 @@ public SplitClientImpl(SplitFactory container,
7673
Evaluator evaluator,
7774
TelemetryEvaluationProducer telemetryEvaluationProducer,
7875
TelemetryConfigProducer telemetryConfigProducer,
79-
FlagSetsFilter flagSetsFilter) {
76+
FlagSetsFilter flagSetsFilter,
77+
FallbackTreatmentCalculator fallbackTreatmentCalculator) {
8078
_container = container;
8179
_splitCacheConsumer = checkNotNull(splitCacheConsumer);
8280
_impressionManager = checkNotNull(impressionManager);
@@ -87,6 +85,7 @@ public SplitClientImpl(SplitFactory container,
8785
_telemetryEvaluationProducer = checkNotNull(telemetryEvaluationProducer);
8886
_telemetryConfigProducer = checkNotNull(telemetryConfigProducer);
8987
_flagSetsFilter = flagSetsFilter;
88+
_fallbackTreatmentCalculator = fallbackTreatmentCalculator;
9089
}
9190

9291
@Override
@@ -492,31 +491,31 @@ private SplitResult getTreatmentWithConfigInternal(String matchingKey, String bu
492491

493492
if (_container.isDestroyed()) {
494493
_log.error(CLIENT_DESTROY);
495-
return SPLIT_RESULT_CONTROL;
494+
return checkFallbackTreatment(featureFlag);
496495
}
497496

498497
if (!KeyValidator.isValid(matchingKey, MATCHING_KEY, _config.maxStringLength(), methodEnum.getMethod())) {
499-
return SPLIT_RESULT_CONTROL;
498+
return checkFallbackTreatment(featureFlag);
500499
}
501500

502501
if (!KeyValidator.bucketingKeyIsValid(bucketingKey, _config.maxStringLength(), methodEnum.getMethod())) {
503-
return SPLIT_RESULT_CONTROL;
502+
return checkFallbackTreatment(featureFlag);
504503
}
505504

506505
Optional<String> splitNameResult = SplitNameValidator.isValid(featureFlag, methodEnum.getMethod());
507506
if (!splitNameResult.isPresent()) {
508-
return SPLIT_RESULT_CONTROL;
507+
return checkFallbackTreatment(featureFlag);
509508
}
510509
featureFlag = splitNameResult.get();
511510
long start = System.currentTimeMillis();
512511

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

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

522521
recordStats(
@@ -541,10 +540,19 @@ private SplitResult getTreatmentWithConfigInternal(String matchingKey, String bu
541540
} catch (Exception e1) {
542541
// ignore
543542
}
544-
return SPLIT_RESULT_CONTROL;
543+
return checkFallbackTreatment(featureFlag);
545544
}
546545
}
547546

547+
private SplitResult checkFallbackTreatment(String featureName) {
548+
FallbackTreatment fallbackTreatment = _fallbackTreatmentCalculator.resolve(featureName, "");
549+
String config = null;
550+
if (fallbackTreatment.getConfig() != null) {
551+
config = fallbackTreatment.getConfig();
552+
}
553+
return new SplitResult(fallbackTreatment.getTreatment(), config);
554+
}
555+
548556
private String validateProperties(Map<String, Object> properties) {
549557
if (properties == null){
550558
return null;
@@ -563,6 +571,7 @@ private Map<String, SplitResult> getTreatmentsWithConfigInternal(String matching
563571
_log.error(String.format("%s: featureFlagNames must be a non-empty array", methodEnum.getMethod()));
564572
return new HashMap<>();
565573
}
574+
566575
try {
567576
checkSDKReady(methodEnum, featureFlagNames);
568577
Map<String, SplitResult> result = validateBeforeEvaluate(featureFlagNames, matchingKey, methodEnum, bucketingKey);
@@ -601,46 +610,43 @@ private Map<String, SplitResult> getTreatmentsBySetsWithConfigInternal(String ma
601610
if (cleanFlagSets.isEmpty()) {
602611
return new HashMap<>();
603612
}
604-
List<String> featureFlagNames = new ArrayList<>();
605-
try {
606-
checkSDKReady(methodEnum);
607-
Map<String, SplitResult> result = validateBeforeEvaluateByFlagSets(matchingKey, methodEnum,bucketingKey);
608-
if(result != null) {
609-
return result;
610-
}
611-
Map<String, EvaluatorImp.TreatmentLabelAndChangeNumber> evaluatorResult = _evaluator.evaluateFeaturesByFlagSets(matchingKey,
612-
bucketingKey, new ArrayList<>(cleanFlagSets), attributes);
613+
checkSDKReady(methodEnum);
614+
Map<String, SplitResult> result = validateBeforeEvaluateByFlagSets(matchingKey, methodEnum,bucketingKey);
615+
if(result != null) {
616+
return result;
617+
}
618+
Map<String, EvaluatorImp.TreatmentLabelAndChangeNumber> evaluatorResult = _evaluator.evaluateFeaturesByFlagSets(matchingKey,
619+
bucketingKey, new ArrayList<>(cleanFlagSets), attributes);
613620

614-
return processEvaluatorResult(evaluatorResult, methodEnum, matchingKey, bucketingKey, attributes, initTime,
615-
validateProperties(evaluationOptions.getProperties()));
616-
} catch (Exception e) {
617-
try {
621+
evaluatorResult.entrySet().forEach(flag -> {
622+
if (flag.getValue().label != null &&
623+
flag.getValue().label.contains(io.split.engine.evaluator.Labels.EXCEPTION)) {
618624
_telemetryEvaluationProducer.recordException(methodEnum);
619-
_log.error(CATCHALL_EXCEPTION, e);
620-
} catch (Exception e1) {
621-
// ignore
622625
}
623-
return createMapControl(featureFlagNames);
624-
}
626+
});
627+
return processEvaluatorResult(evaluatorResult, methodEnum, matchingKey, bucketingKey, attributes, initTime,
628+
validateProperties(evaluationOptions.getProperties()));
625629
}
630+
626631
private Map<String, SplitResult> processEvaluatorResult(Map<String, EvaluatorImp.TreatmentLabelAndChangeNumber> evaluatorResult,
627632
MethodEnum methodEnum, String matchingKey, String bucketingKey, Map<String,
628633
Object> attributes, long initTime, String properties){
629634
List<DecoratedImpression> decoratedImpressions = new ArrayList<>();
630635
Map<String, SplitResult> result = new HashMap<>();
631-
evaluatorResult.keySet().forEach(t -> {
632-
if (evaluatorResult.get(t).treatment.equals(Treatments.CONTROL) && evaluatorResult.get(t).label.
633-
equals(Labels.DEFINITION_NOT_FOUND) && _gates.isSDKReady()) {
636+
evaluatorResult.keySet().forEach(flag -> {
637+
if (evaluatorResult.get(flag).label != null &&
638+
evaluatorResult.get(flag).label.contains(Labels.DEFINITION_NOT_FOUND) &&
639+
_gates.isSDKReady()) {
634640
_log.warn(String.format("%s: you passed \"%s\" that does not exist in this environment please double check " +
635-
"what feature flags exist in the Split user interface.", methodEnum.getMethod(), t));
636-
result.put(t, SPLIT_RESULT_CONTROL);
641+
"what feature flags exist in the Split user interface.", methodEnum.getMethod(), flag));
642+
result.put(flag, checkFallbackTreatment(flag));
637643
} else {
638-
result.put(t, new SplitResult(evaluatorResult.get(t).treatment, evaluatorResult.get(t).configurations));
644+
result.put(flag, new SplitResult(evaluatorResult.get(flag).treatment, evaluatorResult.get(flag).configurations));
639645
decoratedImpressions.add(
640646
new DecoratedImpression(
641-
new Impression(matchingKey, bucketingKey, t, evaluatorResult.get(t).treatment, System.currentTimeMillis(),
642-
evaluatorResult.get(t).label, evaluatorResult.get(t).changeNumber, attributes, properties),
643-
evaluatorResult.get(t).track));
647+
new Impression(matchingKey, bucketingKey, flag, evaluatorResult.get(flag).treatment, System.currentTimeMillis(),
648+
evaluatorResult.get(flag).label, evaluatorResult.get(flag).changeNumber, attributes, properties),
649+
evaluatorResult.get(flag).track));
644650
}
645651
});
646652
_telemetryEvaluationProducer.recordLatency(methodEnum, System.currentTimeMillis() - initTime);
@@ -735,7 +741,7 @@ private void checkSDKReady(MethodEnum methodEnum) {
735741

736742
private Map<String, SplitResult> createMapControl(List<String> featureFlags) {
737743
Map<String, SplitResult> result = new HashMap<>();
738-
featureFlags.forEach(s -> result.put(s, SPLIT_RESULT_CONTROL));
744+
featureFlags.forEach(s -> result.put(s, checkFallbackTreatment(s)));
739745
return result;
740746
}
741747
}

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

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.google.common.io.Files;
44
import io.split.client.dtos.BearerCredentialsProvider;
5+
import io.split.client.dtos.FallbackTreatmentCalculatorImp;
56
import io.split.client.dtos.Metadata;
67
import io.split.client.events.EventsSender;
78
import io.split.client.events.EventsStorage;
@@ -256,8 +257,9 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
256257
_telemetrySyncTask = new TelemetrySyncTask(config.getTelemetryRefreshRate(), _telemetrySynchronizer,
257258
config.getThreadFactory());
258259

260+
FallbackTreatmentCalculatorImp fallbackTreatmentCalculatorImp = new FallbackTreatmentCalculatorImp(null);
259261
// Evaluator
260-
_evaluator = new EvaluatorImp(splitCache, segmentCache, ruleBasedSegmentCache, null);
262+
_evaluator = new EvaluatorImp(splitCache, segmentCache, ruleBasedSegmentCache, fallbackTreatmentCalculatorImp);
261263

262264
// SplitClient
263265
_client = new SplitClientImpl(this,
@@ -269,7 +271,9 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
269271
_evaluator,
270272
_telemetryStorageProducer, // TelemetryEvaluation instance
271273
_telemetryStorageProducer, // TelemetryConfiguration instance
272-
flagSetsFilter);
274+
flagSetsFilter,
275+
fallbackTreatmentCalculatorImp
276+
);
273277

274278
// SplitManager
275279
_manager = new SplitManagerImpl(splitCache, config, _gates, _telemetryStorageProducer);
@@ -348,8 +352,9 @@ protected SplitFactoryImpl(String apiToken, SplitClientConfig config, CustomStor
348352
_telemetrySynchronizer = new TelemetryConsumerSubmitter(customStorageWrapper, _sdkMetadata);
349353
UserCustomRuleBasedSegmentAdapterConsumer userCustomRuleBasedSegmentAdapterConsumer =
350354
new UserCustomRuleBasedSegmentAdapterConsumer(customStorageWrapper);
355+
FallbackTreatmentCalculatorImp fallbackTreatmentCalculatorImp = new FallbackTreatmentCalculatorImp(null);
351356
_evaluator = new EvaluatorImp(userCustomSplitAdapterConsumer, userCustomSegmentAdapterConsumer,
352-
userCustomRuleBasedSegmentAdapterConsumer, null);
357+
userCustomRuleBasedSegmentAdapterConsumer, fallbackTreatmentCalculatorImp);
353358
_impressionsSender = PluggableImpressionSender.create(customStorageWrapper);
354359
_uniqueKeysTracker = createUniqueKeysTracker(config);
355360
_impressionsManager = buildImpressionsManager(config, userCustomImpressionAdapterConsumer,
@@ -378,7 +383,9 @@ protected SplitFactoryImpl(String apiToken, SplitClientConfig config, CustomStor
378383
_evaluator,
379384
_telemetryStorageProducer, // TelemetryEvaluation instance
380385
_telemetryStorageProducer, // TelemetryConfiguration instance
381-
flagSetsFilter);
386+
flagSetsFilter,
387+
fallbackTreatmentCalculatorImp
388+
);
382389

383390
// SyncManager
384391
_syncManager = new ConsumerSyncManager(synchronizer);
@@ -446,8 +453,9 @@ protected SplitFactoryImpl(SplitClientConfig config) {
446453
SplitTasks splitTasks = SplitTasks.build(_splitSynchronizationTask, _segmentSynchronizationTaskImp,
447454
_impressionsManager, null, null, null);
448455

456+
FallbackTreatmentCalculatorImp fallbackTreatmentCalculatorImp = new FallbackTreatmentCalculatorImp(null);
449457
// Evaluator
450-
_evaluator = new EvaluatorImp(splitCache, segmentCache, ruleBasedSegmentCache, null);
458+
_evaluator = new EvaluatorImp(splitCache, segmentCache, ruleBasedSegmentCache, fallbackTreatmentCalculatorImp);
451459

452460
EventsStorage eventsStorage = new NoopEventsStorageImp();
453461

@@ -461,7 +469,9 @@ protected SplitFactoryImpl(SplitClientConfig config) {
461469
_evaluator,
462470
_telemetryStorageProducer, // TelemetryEvaluation instance
463471
_telemetryStorageProducer, // TelemetryConfiguration instance
464-
flagSetsFilter);
472+
flagSetsFilter,
473+
fallbackTreatmentCalculatorImp
474+
);
465475

466476
// Synchronizer
467477
Synchronizer synchronizer = new LocalhostSynchronizer(splitTasks, _splitFetcher,

client/src/main/java/io/split/client/dtos/FallbackTreatment.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
package io.split.client.dtos;
22

3-
import java.util.Map;
4-
53
public class FallbackTreatment {
6-
private final Map<String, Object> _config;
4+
private final String _config;
75
private final String _treatment;
86
private final String _label;
97

10-
public FallbackTreatment(String treatment, Map<String, Object> config) {
8+
public FallbackTreatment(String treatment, String config) {
119
_treatment = treatment;
1210
_config = config;
1311
_label = null;
@@ -19,13 +17,13 @@ public FallbackTreatment(String treatment) {
1917
_label = null;
2018
}
2119

22-
public FallbackTreatment(String treatment, Map<String, Object> config, String label) {
20+
public FallbackTreatment(String treatment, String config, String label) {
2321
_treatment = treatment;
2422
_config = config;
2523
_label = label;
2624
}
2725

28-
public Map<String, Object> getConfig() {
26+
public String getConfig() {
2927
return _config;
3028
}
3129

client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,10 @@
33
import io.split.client.dtos.ConditionType;
44
import io.split.client.dtos.FallbackTreatment;
55
import io.split.client.dtos.FallbackTreatmentCalculator;
6-
import io.split.client.dtos.FallbackTreatmentsConfiguration;
76
import io.split.client.exceptions.ChangeNumberExceptionWrapper;
87
import io.split.engine.experiments.ParsedCondition;
98
import io.split.engine.experiments.ParsedSplit;
109
import io.split.engine.splitter.Splitter;
11-
import io.split.grammar.Treatments;
1210
import io.split.storages.RuleBasedSegmentCacheConsumer;
1311
import io.split.storages.SegmentCacheConsumer;
1412
import io.split.storages.SplitCacheConsumer;
@@ -63,7 +61,27 @@ public Map<String, TreatmentLabelAndChangeNumber> evaluateFeatures(String matchi
6361
public Map<String, EvaluatorImp.TreatmentLabelAndChangeNumber> evaluateFeaturesByFlagSets(String key, String bucketingKey,
6462
List<String> flagSets, Map<String, Object> attributes) {
6563
List<String> flagSetsWithNames = getFeatureFlagNamesByFlagSets(flagSets);
66-
return evaluateFeatures(key, bucketingKey, flagSetsWithNames, attributes);
64+
try {
65+
return evaluateFeatures(key, bucketingKey, flagSetsWithNames, attributes);
66+
} catch (Exception e) {
67+
_log.error("Evaluator Exception", e);
68+
return createMapControl(flagSetsWithNames, io.split.engine.evaluator.Labels.EXCEPTION);
69+
}
70+
}
71+
72+
private Map<String, EvaluatorImp.TreatmentLabelAndChangeNumber> createMapControl(List<String> featureFlags, String label) {
73+
Map<String, TreatmentLabelAndChangeNumber> result = new HashMap<>();
74+
featureFlags.forEach(s -> result.put(s, checkFallbackTreatment(s, label)));
75+
return result;
76+
}
77+
78+
private EvaluatorImp.TreatmentLabelAndChangeNumber checkFallbackTreatment(String featureName, String label) {
79+
FallbackTreatment fallbackTreatment = _fallbackTreatmentCalculator.resolve(featureName, label);
80+
return new EvaluatorImp.TreatmentLabelAndChangeNumber(fallbackTreatment.getTreatment(),
81+
fallbackTreatment.getLabel(),
82+
null,
83+
getFallbackConfig(fallbackTreatment),
84+
false);
6785
}
6886

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

198+
private String getFallbackConfig(FallbackTreatment fallbackTreatment) {
199+
if (fallbackTreatment.getConfig() != null) {
200+
return fallbackTreatment.getConfig();
201+
}
202+
203+
return null;
204+
}
205+
180206
private TreatmentLabelAndChangeNumber evaluateParsedSplit(String matchingKey, String bucketingKey, Map<String, Object> attributes,
181207
ParsedSplit parsedSplit, String featureName) {
182208
try {
183-
184209
if (parsedSplit == null) {
185210
FallbackTreatment fallbackTreatment = _fallbackTreatmentCalculator.resolve(featureName, Labels.DEFINITION_NOT_FOUND);
186-
return new TreatmentLabelAndChangeNumber(fallbackTreatment.getTreatment(), fallbackTreatment.getLabel());
211+
return new TreatmentLabelAndChangeNumber(fallbackTreatment.getTreatment(),
212+
fallbackTreatment.getLabel(),
213+
null,
214+
getFallbackConfig(fallbackTreatment),
215+
false);
187216
}
188217
return getTreatment(matchingKey, bucketingKey, parsedSplit, attributes);
189218
} catch (ChangeNumberExceptionWrapper e) {

0 commit comments

Comments
 (0)