Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
2 changes: 1 addition & 1 deletion client/src/main/java/io/split/Spec.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ private Spec() {
}

// TODO: Change the schema to 1.3 when updating splitclient
public static String SPEC_VERSION = "1.1";
public static final String SPEC_1_3 = "1.3";
public static final String SPEC_1_1 = "1.1";
public static String SPEC_VERSION = SPEC_1_3;
}

Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import io.split.engine.matchers.CombiningMatcher;
import io.split.engine.matchers.strings.WhitelistMatcher;
import io.split.grammar.Treatments;
import io.split.storages.SplitCacheConsumer;
import io.split.storages.SplitCacheProducer;

import java.util.ArrayList;
Expand Down
31 changes: 18 additions & 13 deletions client/src/main/java/io/split/client/HttpSplitChangeFetcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public final class HttpSplitChangeFetcher implements SplitChangeFetcher {
private static final Logger _log = LoggerFactory.getLogger(HttpSplitChangeFetcher.class);

private static final String SINCE = "since";
private static final String RB_SINCE = "rbSince";
private static final String TILL = "till";
private static final String SETS = "sets";
private static final String SPEC = "s";
Expand All @@ -56,27 +57,18 @@ long makeRandomTill() {
}

@Override
public SplitChange fetch(long since, FetchOptions options) {

public SplitChange fetch(long since, long sinceRBS, FetchOptions options) {
long start = System.currentTimeMillis();

try {
URIBuilder uriBuilder = new URIBuilder(_target).addParameter(SPEC, "" + SPEC_VERSION);
uriBuilder.addParameter(SINCE, "" + since);
if (!options.flagSetsFilter().isEmpty()) {
uriBuilder.addParameter(SETS, "" + options.flagSetsFilter());
}
if (options.hasCustomCN()) {
uriBuilder.addParameter(TILL, "" + options.targetCN());
}
URI uri = uriBuilder.build();
URI uri = buildURL(options, since, sinceRBS);
SplitHttpResponse response = _client.get(uri, options, null);

if (response.statusCode() < HttpStatus.SC_OK || response.statusCode() >= HttpStatus.SC_MULTIPLE_CHOICES) {
if (response.statusCode() == HttpStatus.SC_REQUEST_URI_TOO_LONG) {
_log.error("The amount of flag sets provided are big causing uri length error.");
throw new UriTooLongException(String.format("Status code: %s. Message: %s", response.statusCode(), response.statusMessage()));
}

_telemetryRuntimeProducer.recordSyncError(ResourceEnum.SPLIT_SYNC, response.statusCode());
throw new IllegalStateException(
String.format("Could not retrieve splitChanges since %s; http return code %s", since, response.statusCode())
Expand All @@ -86,8 +78,21 @@ public SplitChange fetch(long since, FetchOptions options) {
} catch (Exception e) {
throw new IllegalStateException(String.format("Problem fetching splitChanges since %s: %s", since, e), e);
} finally {
_telemetryRuntimeProducer.recordSyncLatency(HTTPLatenciesEnum.SPLITS, System.currentTimeMillis()-start);
_telemetryRuntimeProducer.recordSyncLatency(HTTPLatenciesEnum.SPLITS, System.currentTimeMillis() - start);
}
}

private URI buildURL(FetchOptions options, long since, long sinceRBS) throws URISyntaxException {
URIBuilder uriBuilder = new URIBuilder(_target).addParameter(SPEC, "" + SPEC_VERSION);
uriBuilder.addParameter(SINCE, "" + since);
uriBuilder.addParameter(RB_SINCE, "" + sinceRBS);
if (!options.flagSetsFilter().isEmpty()) {
uriBuilder.addParameter(SETS, "" + options.flagSetsFilter());
}
if (options.hasCustomCN()) {
uriBuilder.addParameter(TILL, "" + options.targetCN());
}
return uriBuilder.build();
}

@VisibleForTesting
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package io.split.client;

import com.google.gson.stream.JsonReader;
import io.split.client.dtos.ChangeDto;
import io.split.client.dtos.SplitChange;
import io.split.client.utils.InputStreamProvider;
import io.split.client.utils.Json;
import io.split.client.utils.LocalhostSanitizer;
import io.split.engine.common.FetchOptions;
import io.split.engine.experiments.SplitChangeFetcher;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -15,6 +17,7 @@
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;

public class JsonLocalhostSplitChangeFetcher implements SplitChangeFetcher {
Expand All @@ -29,10 +32,14 @@ public JsonLocalhostSplitChangeFetcher(InputStreamProvider inputStreamProvider)
}

@Override
public SplitChange fetch(long since, FetchOptions options) {
public SplitChange fetch(long since, long sinceRBS, FetchOptions options) {
try {
JsonReader jsonReader = new JsonReader(new BufferedReader(new InputStreamReader(_inputStreamProvider.get(), StandardCharsets.UTF_8)));
SplitChange splitChange = Json.fromJson(jsonReader, SplitChange.class);
splitChange.ruleBasedSegments = new ChangeDto<>();
splitChange.ruleBasedSegments.d = new ArrayList<>();
splitChange.ruleBasedSegments.t = -1;
splitChange.ruleBasedSegments.s = -1;
return processSplitChange(splitChange, since);
} catch (Exception e) {
throw new IllegalStateException("Problem fetching splitChanges: " + e.getMessage(), e);
Expand All @@ -42,22 +49,22 @@ public SplitChange fetch(long since, FetchOptions options) {
private SplitChange processSplitChange(SplitChange splitChange, long changeNumber) throws NoSuchAlgorithmException {
SplitChange splitChangeToProcess = LocalhostSanitizer.sanitization(splitChange);
// if the till is less than storage CN and different from the default till ignore the change
if (splitChangeToProcess.till < changeNumber && splitChangeToProcess.till != -1) {
if (splitChangeToProcess.featureFlags.t < changeNumber && splitChangeToProcess.featureFlags.t != -1) {
_log.warn("The till is lower than the change number or different to -1");
return null;
}
String splitJson = splitChange.splits.toString();
String splitJson = splitChange.featureFlags.d.toString();
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.reset();
digest.update(splitJson.getBytes());
// calculate the json sha
byte [] currHash = digest.digest();
//if sha exist and is equal to before sha, or if till is equal to default till returns the same segmentChange with till equals to storage CN
if (Arrays.equals(lastHash, currHash) || splitChangeToProcess.till == -1) {
splitChangeToProcess.till = changeNumber;
if (Arrays.equals(lastHash, currHash) || splitChangeToProcess.featureFlags.t == -1) {
splitChangeToProcess.featureFlags.t = changeNumber;
}
lastHash = currHash;
splitChangeToProcess.since = changeNumber;
splitChangeToProcess.featureFlags.s = changeNumber;
return splitChangeToProcess;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import io.split.client.dtos.Split;
import io.split.client.dtos.SplitChange;
import io.split.client.dtos.Status;
import io.split.client.dtos.ChangeDto;
import io.split.client.utils.LocalhostConstants;
import io.split.client.utils.LocalhostSanitizer;
import io.split.engine.common.FetchOptions;
Expand Down Expand Up @@ -34,11 +35,12 @@ public LegacyLocalhostSplitChangeFetcher(String directory) {
}

@Override
public SplitChange fetch(long since, FetchOptions options) {
public SplitChange fetch(long since, long sinceRBS, FetchOptions options) {

try (BufferedReader reader = new BufferedReader(new FileReader(_splitFile))) {
SplitChange splitChange = new SplitChange();
splitChange.splits = new ArrayList<>();
splitChange.featureFlags = new ChangeDto<>();
splitChange.featureFlags.d = new ArrayList<>();
for (String line = reader.readLine(); line != null; line = reader.readLine()) {
String lineTrim = line.trim();
if (lineTrim.isEmpty() || lineTrim.startsWith("#")) {
Expand All @@ -51,15 +53,16 @@ public SplitChange fetch(long since, FetchOptions options) {
_log.info("Ignoring line since it does not have 2 or 3 columns: " + lineTrim);
continue;
}
Optional<Split> splitOptional = splitChange.splits.stream().filter(split -> split.name.equals(featureTreatment[0])).findFirst();
Optional<Split> splitOptional = splitChange.featureFlags.d.stream().
filter(split -> split.name.equals(featureTreatment[0])).findFirst();
Split split = splitOptional.orElse(null);
if(split == null) {
split = new Split();
split.name = featureTreatment[0];
split.configurations = new HashMap<>();
split.conditions = new ArrayList<>();
} else {
splitChange.splits.remove(split);
splitChange.featureFlags.d.remove(split);
}
split.status = Status.ACTIVE;
split.defaultTreatment = featureTreatment[1];
Expand All @@ -78,10 +81,14 @@ public SplitChange fetch(long since, FetchOptions options) {
} else {
split.conditions.add(condition);
}
splitChange.splits.add(split);
splitChange.featureFlags.d.add(split);
}
splitChange.till = since;
splitChange.since = since;
splitChange.featureFlags.t = since;
splitChange.featureFlags.s = since;
splitChange.ruleBasedSegments = new ChangeDto<>();
splitChange.ruleBasedSegments.s = -1;
splitChange.ruleBasedSegments.t = -1;
splitChange.ruleBasedSegments.d = new ArrayList<>();
return splitChange;
} catch (FileNotFoundException f) {
_log.warn("There was no file named " + _splitFile.getPath() + " found. " +
Expand Down
3 changes: 0 additions & 3 deletions client/src/main/java/io/split/client/SplitClientImpl.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package io.split.client;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParser;
import io.split.client.api.Key;
import io.split.client.api.SplitResult;
import io.split.client.dtos.DecoratedImpression;
Expand All @@ -26,7 +24,6 @@
import io.split.telemetry.domain.enums.MethodEnum;
import io.split.telemetry.storage.TelemetryConfigProducer;
import io.split.telemetry.storage.TelemetryEvaluationProducer;
import io.split.client.utils.Json;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import io.split.inputValidation.ApiKeyValidator;
import io.split.grammar.Treatments;
import io.split.service.SplitHttpClient;
import io.split.storages.enums.StorageMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down
14 changes: 10 additions & 4 deletions client/src/main/java/io/split/client/SplitFactoryImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import io.split.engine.experiments.SplitFetcherImp;
import io.split.engine.experiments.SplitParser;
import io.split.engine.experiments.SplitSynchronizationTask;
import io.split.engine.experiments.RuleBasedSegmentParser;
import io.split.engine.segments.SegmentChangeFetcher;
import io.split.engine.segments.SegmentSynchronizationTaskImp;
import io.split.integrations.IntegrationsConfig;
Expand All @@ -68,6 +69,7 @@
import io.split.storages.SplitCacheProducer;
import io.split.storages.RuleBasedSegmentCacheConsumer;
import io.split.storages.RuleBasedSegmentCache;
import io.split.storages.RuleBasedSegmentCacheProducer;
import io.split.storages.enums.OperationMode;
import io.split.storages.memory.InMemoryCacheImp;
import io.split.storages.memory.SegmentCacheInMemoryImpl;
Expand Down Expand Up @@ -220,8 +222,10 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn


SplitParser splitParser = new SplitParser();
RuleBasedSegmentParser ruleBasedSegmentParser = new RuleBasedSegmentParser();
// SplitFetcher
_splitFetcher = buildSplitFetcher(splitCache, splitParser, flagSetsFilter);
_splitFetcher = buildSplitFetcher(splitCache, splitParser, flagSetsFilter,
ruleBasedSegmentParser, ruleBasedSegmentCache);

// SplitSynchronizationTask
_splitSynchronizationTask = new SplitSynchronizationTask(_splitFetcher,
Expand Down Expand Up @@ -422,9 +426,10 @@ protected SplitFactoryImpl(SplitClientConfig config) {
// SplitFetcher
SplitChangeFetcher splitChangeFetcher = createSplitChangeFetcher(config);
SplitParser splitParser = new SplitParser();
RuleBasedSegmentParser ruleBasedSegmentParser = new RuleBasedSegmentParser();

_splitFetcher = new SplitFetcherImp(splitChangeFetcher, splitParser, splitCache, _telemetryStorageProducer,
flagSetsFilter);
flagSetsFilter, ruleBasedSegmentParser, _ruleBasedSegmentCache);

// SplitSynchronizationTask
_splitSynchronizationTask = new SplitSynchronizationTask(_splitFetcher, splitCache,
Expand Down Expand Up @@ -617,11 +622,12 @@ private SegmentSynchronizationTaskImp buildSegments(SplitClientConfig config,
}

private SplitFetcher buildSplitFetcher(SplitCacheProducer splitCacheProducer, SplitParser splitParser,
FlagSetsFilter flagSetsFilter) throws URISyntaxException {
FlagSetsFilter flagSetsFilter, RuleBasedSegmentParser ruleBasedSegmentParser,
RuleBasedSegmentCacheProducer ruleBasedSegmentCache) throws URISyntaxException {
SplitChangeFetcher splitChangeFetcher = HttpSplitChangeFetcher.create(_splitHttpClient, _rootTarget,
_telemetryStorageProducer);
return new SplitFetcherImp(splitChangeFetcher, splitParser, splitCacheProducer, _telemetryStorageProducer,
flagSetsFilter);
flagSetsFilter, ruleBasedSegmentParser, ruleBasedSegmentCache);
}

private ImpressionsManagerImpl buildImpressionsManager(SplitClientConfig config,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import io.split.client.dtos.Split;
import io.split.client.dtos.SplitChange;
import io.split.client.dtos.Status;
import io.split.client.dtos.ChangeDto;
import io.split.client.utils.InputStreamProvider;
import io.split.client.utils.LocalhostConstants;
import io.split.engine.common.FetchOptions;
Expand Down Expand Up @@ -32,25 +33,27 @@ public YamlLocalhostSplitChangeFetcher(InputStreamProvider inputStreamProvider)
}

@Override
public SplitChange fetch(long since, FetchOptions options) {
public SplitChange fetch(long since, long sinceRBS, FetchOptions options) {
try {
Yaml yaml = new Yaml();
List<Map<String, Map<String, Object>>> yamlSplits = yaml.load(_inputStreamProvider.get());
SplitChange splitChange = new SplitChange();
splitChange.splits = new ArrayList<>();
splitChange.featureFlags = new ChangeDto<>();
splitChange.featureFlags.d = new ArrayList<>();
for(Map<String, Map<String, Object>> aSplit : yamlSplits) {
// The outter map is a map with one key, the split name
Map.Entry<String, Map<String, Object>> splitAndValues = aSplit.entrySet().iterator().next();

Optional<Split> splitOptional = splitChange.splits.stream().filter(split -> split.name.equals(splitAndValues.getKey())).findFirst();
Optional<Split> splitOptional = splitChange.featureFlags.d.stream().
filter(split -> split.name.equals(splitAndValues.getKey())).findFirst();
Split split = splitOptional.orElse(null);
if(split == null) {
split = new Split();
split.name = splitAndValues.getKey();
split.configurations = new HashMap<>();
split.conditions = new ArrayList<>();
} else {
splitChange.splits.remove(split);
splitChange.featureFlags.d.remove(split);
}
String treatment = (String) splitAndValues.getValue().get("treatment");
String configurations = splitAndValues.getValue().get("config") != null ? (String) splitAndValues.getValue().get("config") : null;
Expand All @@ -68,11 +71,14 @@ public SplitChange fetch(long since, FetchOptions options) {
split.trafficTypeName = LocalhostConstants.USER;
split.trafficAllocation = LocalhostConstants.SIZE_100;
split.trafficAllocationSeed = LocalhostConstants.SIZE_1;

splitChange.splits.add(split);
splitChange.featureFlags.d.add(split);
}
splitChange.till = since;
splitChange.since = since;
splitChange.featureFlags.t = since;
splitChange.featureFlags.s = since;
splitChange.ruleBasedSegments = new ChangeDto<>();
splitChange.ruleBasedSegments.s = -1;
splitChange.ruleBasedSegments.t = -1;
splitChange.ruleBasedSegments.d = new ArrayList<>();
return splitChange;
} catch (Exception e) {
throw new IllegalStateException("Problem fetching splitChanges using a yaml file: " + e.getMessage(), e);
Expand Down
9 changes: 9 additions & 0 deletions client/src/main/java/io/split/client/dtos/ChangeDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.split.client.dtos;

import java.util.List;

public class ChangeDto<T> {
public long s;
public long t;
public List<T> d;
}
9 changes: 5 additions & 4 deletions client/src/main/java/io/split/client/dtos/SplitChange.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package io.split.client.dtos;

import java.util.List;
import com.google.gson.annotations.SerializedName;

public class SplitChange {
public List<Split> splits;
public long since;
public long till;
@SerializedName("ff")
public ChangeDto<Split> featureFlags;
@SerializedName("rbs")
public ChangeDto<RuleBasedSegment> ruleBasedSegments;
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ public static<T> void process(List<T> data, URI endpoint, CloseableHttpClient cl

}
}

Loading