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
44 changes: 25 additions & 19 deletions client/src/main/java/io/split/client/SplitClientImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -510,12 +510,15 @@ private SplitResult getTreatmentWithConfigInternal(String matchingKey, String bu
long start = System.currentTimeMillis();

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

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 checkFallbackTreatment(featureFlag);
String label = result.label;
if (result.label != null && result.label.contains(Labels.DEFINITION_NOT_FOUND)) {
if (_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 checkFallbackTreatment(featureFlag);
}
label = result.label.replace(Labels.DEFINITION_NOT_FOUND, Labels.NOT_READY);
}

recordStats(
Expand All @@ -525,7 +528,7 @@ private SplitResult getTreatmentWithConfigInternal(String matchingKey, String bu
start,
result.treatment,
String.format("sdk.%s", methodEnum.getMethod()),
_config.labelsEnabled() ? result.label : null,
_config.labelsEnabled() ? label : null,
result.changeNumber,
attributes,
result.track,
Expand Down Expand Up @@ -634,20 +637,23 @@ private Map<String, SplitResult> processEvaluatorResult(Map<String, EvaluatorImp
List<DecoratedImpression> decoratedImpressions = new ArrayList<>();
Map<String, SplitResult> result = new HashMap<>();
evaluatorResult.keySet().forEach(flag -> {
String label = evaluatorResult.get(flag).label;
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(), flag));
result.put(flag, checkFallbackTreatment(flag));
} else {
result.put(flag, new SplitResult(evaluatorResult.get(flag).treatment, evaluatorResult.get(flag).configurations));
decoratedImpressions.add(
new DecoratedImpression(
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));
evaluatorResult.get(flag).label.contains(Labels.DEFINITION_NOT_FOUND)) {
if (_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(), flag));
result.put(flag, checkFallbackTreatment(flag));
return;
}
label = evaluatorResult.get(flag).label.replace(Labels.DEFINITION_NOT_FOUND, Labels.NOT_READY);
}
result.put(flag, new SplitResult(evaluatorResult.get(flag).treatment, evaluatorResult.get(flag).configurations));
decoratedImpressions.add(
new DecoratedImpression(
new Impression(matchingKey, bucketingKey, flag, evaluatorResult.get(flag).treatment, System.currentTimeMillis(),
label, evaluatorResult.get(flag).changeNumber, attributes, properties),
evaluatorResult.get(flag).track));
});
_telemetryEvaluationProducer.recordLatency(methodEnum, System.currentTimeMillis() - initTime);
if (!decoratedImpressions.isEmpty()) {
Expand Down
1 change: 1 addition & 0 deletions client/src/main/java/io/split/engine/evaluator/Labels.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ public class Labels {
public static final String EXCEPTION = "exception";
public static final String UNSUPPORTED_MATCHER = "targeting rule type unsupported by sdk";
public static final String PREREQUISITES_NOT_MET = "prerequisites not met";
public static final String NOT_READY = "not ready";
}
Original file line number Diff line number Diff line change
Expand Up @@ -1393,6 +1393,83 @@ public MockResponse dispatch(RecordedRequest request) {
Assert.assertTrue(check1);
}

@Test
public void FallbackTreatmentNotReadyTest() throws Exception {
String splits = new String(Files.readAllBytes(Paths.get("src/test/resources/splits_imp_toggle.json")), StandardCharsets.UTF_8);
List<RecordedRequest> allRequests = new ArrayList<>();
Dispatcher dispatcher = new Dispatcher() {
@Override
public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
allRequests.add(request);
switch (request.getPath()) {
case "/api/splitChanges?s=1.3&since=-1&rbSince=-1":
Thread.sleep(1000);
return new MockResponse().setResponseCode(200).setBody(splits);
case "/api/splitChanges?s=1.3&since=1602796638344&rbSince=-1":
return new MockResponse().setResponseCode(200).setBody("{\"ff\":{\"d\":[], \"s\":1602796638344, \"t\":1602796638344}, \"rbs\":{\"d\":[],\"s\":-1,\"t\":-1}}");
case "/api/testImpressions/bulk":
return new MockResponse().setResponseCode(200);
case "/api/testImpressions/count":
return new MockResponse().setResponseCode(200);
case "/v1/keys/ss":
return new MockResponse().setResponseCode(200);
case "/v1/metrics/usage":
return new MockResponse().setResponseCode(200);
case "/v1/metrics/config":
return new MockResponse().setResponseCode(200);
}
return new MockResponse().setResponseCode(404);
}
};

MockWebServer server = new MockWebServer();
server.setDispatcher(dispatcher);

server.start();
String serverURL = String.format("http://%s:%s", server.getHostName(), server.getPort());
FallbackTreatmentsConfiguration fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(new FallbackTreatment("on-fallback", "{\"prop1\", \"val1\"}"),
null);

SplitClientConfig config = SplitClientConfig.builder()
.setBlockUntilReadyTimeout(10000)
.endpoint(serverURL, serverURL)
.telemetryURL(serverURL + "/v1")
.authServiceURL(String.format("%s/api/auth/enabled", serverURL))
.streamingEnabled(false)
.featuresRefreshRate(5)
.impressionsMode(ImpressionsManager.Mode.DEBUG)
.fallbackTreatments(fallbackTreatmentsConfiguration)
.build();

SplitFactory factory = SplitFactoryBuilder.build("fake-api-token", config);
SplitClient client = factory.client();

Assert.assertEquals("on-fallback", client.getTreatment("user1", "without_impression_toggle"));
Assert.assertEquals("on-fallback", client.getTreatment("user2", "feature"));
client.blockUntilReady();

client.destroy();
boolean check1 = false, check2 = false;
for (int i=0; i < allRequests.size(); i++ ) {
if (allRequests.get(i).getPath().equals("/api/testImpressions/bulk") ) {
String body = allRequests.get(i).getBody().readUtf8();
if (body.contains("user2")) {
check1 = true;
Assert.assertTrue(body.contains("feature"));
Assert.assertTrue(body.contains("fallback - not ready"));
}
if (body.contains("user1")) {
check2 = true;
Assert.assertTrue(body.contains("without_impression_toggle"));
Assert.assertTrue(body.contains("fallback - not ready"));
}
}
}
server.shutdown();
Assert.assertTrue(check1);
Assert.assertTrue(check2);
}

private SSEMockServer buildSSEMockServer(SSEMockServer.SseEventQueue eventQueue) {
return new SSEMockServer(eventQueue, (token, version, channel) -> {
if (!"1.1".equals(version)) {
Expand Down