Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
42 changes: 13 additions & 29 deletions client/src/main/java/io/split/client/HttpSplitChangeFetcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@
import io.split.Spec;
import io.split.client.dtos.SplitChange;
import io.split.client.dtos.SplitHttpResponse;
import io.split.client.dtos.RuleBasedSegment;
import io.split.client.dtos.SplitChangesOldPayloadDto;
import io.split.client.dtos.ChangeDto;
import io.split.client.dtos.Split;
import io.split.client.exceptions.UriTooLongException;
import io.split.client.utils.Json;
import io.split.client.utils.Utils;
Expand All @@ -25,7 +22,6 @@

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;

import static com.google.common.base.Preconditions.checkNotNull;
import static io.split.Spec.SPEC_1_3;
Expand Down Expand Up @@ -92,7 +88,7 @@ public SplitChange fetch(long since, long sinceRBS, FetchOptions options) {
_log.warn("Detected proxy without support for Feature flags spec {} version, will switch to spec version {}",
SPEC_1_3, SPEC_1_1);
_lastProxyCheckTimestamp = System.currentTimeMillis();
return fetch(since, 0, options);
return fetch(since, sinceRBS, options);
}

_telemetryRuntimeProducer.recordSyncError(ResourceEnum.SPLIT_SYNC, response.statusCode());
Expand All @@ -101,44 +97,32 @@ public SplitChange fetch(long since, long sinceRBS, FetchOptions options) {
);
}

String body = response.body();
SplitChange splitChange = new SplitChange();
if (specVersion.equals(Spec.SPEC_1_1)) {
return Json.fromJson(body, SplitChangesOldPayloadDto.class).toSplitChange();
splitChange = convertBodyToOldSpec(response.body());
} else {
if (_lastProxyCheckTimestamp != 0) {
splitChange.clearCache = true;
_lastProxyCheckTimestamp = 0L;
}
splitChange = Json.fromJson(response.body(), SplitChange.class);
}

return Json.fromJson(body, SplitChange.class);

return splitChange;
} 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);
}
}

public Long getLastProxyCheckTimestamp() {
return _lastProxyCheckTimestamp;
}

public void setLastProxyCheckTimestamp(long lastProxyCheckTimestamp) {
synchronized (_lock) {
_lastProxyCheckTimestamp = lastProxyCheckTimestamp;
}
}

private ChangeDto<RuleBasedSegment> createEmptyDTO() {
ChangeDto<RuleBasedSegment> dto = new ChangeDto<>();
dto.d = new ArrayList<>();
dto.t = -1;
dto.s = -1;
return dto;
private SplitChange convertBodyToOldSpec(String body) {
return Json.fromJson(body, SplitChangesOldPayloadDto.class).toSplitChange();
}

private URI buildURL(FetchOptions options, long since, long sinceRBS) throws URISyntaxException {
URIBuilder uriBuilder = new URIBuilder(_target).addParameter(SPEC, "" + specVersion);
uriBuilder.addParameter(SINCE, "" + since);
if (specVersion.equals(SPEC_1_3)) {
uriBuilder.addParameter(RB_SINCE, "" + sinceRBS);
}
uriBuilder.addParameter(RB_SINCE, "" + sinceRBS);
if (!options.flagSetsFilter().isEmpty()) {
uriBuilder.addParameter(SETS, "" + options.flagSetsFilter());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package io.split.client;

import com.google.gson.JsonObject;
import com.google.gson.stream.JsonReader;
import io.split.client.dtos.SplitChange;
import io.split.client.dtos.SplitChangesOldPayloadDto;
import io.split.client.utils.InputStreamProvider;
import io.split.client.utils.Json;
import io.split.client.utils.LocalhostSanitizer;
Expand Down Expand Up @@ -37,13 +39,20 @@ public JsonLocalhostSplitChangeFetcher(InputStreamProvider inputStreamProvider)
public SplitChange fetch(long since, long sinceRBS, FetchOptions options) {
try {
JsonReader jsonReader = new JsonReader(new BufferedReader(new InputStreamReader(_inputStreamProvider.get(), StandardCharsets.UTF_8)));
if (checkOldSpec(new JsonReader(new BufferedReader(new InputStreamReader(_inputStreamProvider.get(), StandardCharsets.UTF_8))))) {
return Json.fromJson(jsonReader, SplitChangesOldPayloadDto.class).toSplitChange();
}
SplitChange splitChange = Json.fromJson(jsonReader, SplitChange.class);
return processSplitChange(splitChange, since, sinceRBS);
} catch (Exception e) {
throw new IllegalStateException("Problem fetching splitChanges: " + e.getMessage(), e);
}
}

private boolean checkOldSpec(JsonReader jsonReader) {
return Json.fromJson(jsonReader, JsonObject.class).has("splits");
}

private SplitChange processSplitChange(SplitChange splitChange, long changeNumber, long changeNumberRBS) throws NoSuchAlgorithmException {
SplitChange splitChangeToProcess = LocalhostSanitizer.sanitization(splitChange);
// if the till is less than storage CN and different from the default till ignore the change
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
@@ -1,9 +1,18 @@
package io.split.client.dtos;

import java.util.ArrayList;
import java.util.List;

public class ChangeDto<T> {
public long s;
public long t;
public List<T> d;

public static ChangeDto createEmptyDto() {
ChangeDto dto = new ChangeDto<>();
dto.d = new ArrayList<>();
dto.t = -1;
dto.s = -1;
return dto;
}
}

This file was deleted.

1 change: 1 addition & 0 deletions client/src/main/java/io/split/client/dtos/SplitChange.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ public class SplitChange {
public ChangeDto<Split> featureFlags;
@SerializedName("rbs")
public ChangeDto<RuleBasedSegment> ruleBasedSegments;
public boolean clearCache;
}
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ private Set<String> runWithoutExceptionHandling(FetchOptions options) throws Int
throw new IllegalStateException("SplitChange was null");
}

if (change.clearCache) {
_splitCacheProducer.clear();
_ruleBasedSegmentCacheProducer.clear();
}

if (checkExitConditions(change.featureFlags, _splitCacheProducer.getChangeNumber()) ||
checkExitConditions(change.ruleBasedSegments, _ruleBasedSegmentCacheProducer.getChangeNumber())) {
return segments;
Expand All @@ -142,7 +147,7 @@ private Set<String> runWithoutExceptionHandling(FetchOptions options) throws Int

synchronized (_lock) {
// check state one more time.
if (checkExitConditions(change.featureFlags, _splitCacheProducer.getChangeNumber()) ||
if (checkExitConditions(change.featureFlags, _splitCacheProducer.getChangeNumber()) &&
checkExitConditions(change.ruleBasedSegments, _ruleBasedSegmentCacheProducer.getChangeNumber())) {
// some other thread may have updated the shared state. exit
return segments;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ public interface RuleBasedSegmentCacheProducer {
void setChangeNumber(long changeNumber);
long getChangeNumber();
void update(List<ParsedRuleBasedSegment> toAdd, List<String> toRemove, long changeNumber);
void clear();
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,9 @@ public void kill(String splitName, String defaultTreatment, long changeNumber) {
@Override
public void clear() {
_concurrentMap.clear();
_changeNumber.set(-1);
_concurrentTrafficTypeNameSet.clear();
_flagSets.clear();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ public List<String> ruleBasedSegmentNames() {
return ruleBasedSegmentNamesList;
}

@Override
public void clear() {
_changeNumber.set(-1);
_concurrentMap.clear();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package io.split.storages.pluggable.adapters;

import io.split.client.dtos.RuleBasedSegment;
import io.split.client.utils.Json;
import io.split.engine.experiments.ParsedRuleBasedSegment;
import io.split.storages.RuleBasedSegmentCacheProducer;
import io.split.storages.pluggable.domain.PrefixAdapter;
import io.split.storages.pluggable.domain.UserStorageWrapper;
import io.split.storages.pluggable.utils.Helper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pluggable.CustomStorageWrapper;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

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

public class UserCustomRuleBasedSegmentAdapterProducer implements RuleBasedSegmentCacheProducer {

private static final Logger _log = LoggerFactory.getLogger(UserCustomRuleBasedSegmentAdapterProducer.class);

private final UserStorageWrapper _userStorageWrapper;

public UserCustomRuleBasedSegmentAdapterProducer(CustomStorageWrapper customStorageWrapper) {
_userStorageWrapper = new UserStorageWrapper(checkNotNull(customStorageWrapper));
}

@Override
public long getChangeNumber() {
String wrapperResponse = _userStorageWrapper.get(PrefixAdapter.buildRuleBasedSegmentChangeNumber());
return Helper.responseToLong(wrapperResponse, -1L);
}

@Override
public boolean remove(String ruleBasedSegmentName) {
// NoOp
return true;
}

@Override
public void setChangeNumber(long changeNumber) {
//NoOp
}

@Override
public void clear() {
//NoOp
}

@Override
public void update(List<ParsedRuleBasedSegment> toAdd, List<String> toRemove, long changeNumber) {
//NoOp
}

public Set<String> getSegments() {
//NoOp
return new HashSet<>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -246,10 +246,6 @@ public void testSwitchingToOldSpec() throws URISyntaxException, InvocationTarget
HttpSplitChangeFetcher fetcher = HttpSplitChangeFetcher.create(splitHtpClient, rootTarget,
Mockito.mock(TelemetryRuntimeProducer.class), true);

Field specVersion = fetcher.getClass().getDeclaredField("specVersion");
specVersion.setAccessible(true);
specVersion.set(fetcher, Spec.SPEC_1_1);

SplitChange change = fetcher.fetch(-1, -1, new FetchOptions.Builder().cacheControlHeaders(true).build());

List<ClassicHttpRequest> captured = requestCaptor.getAllValues();
Expand All @@ -264,7 +260,6 @@ public void testSwitchingToOldSpec() throws URISyntaxException, InvocationTarget
Assert.assertEquals(0, change.ruleBasedSegments.d.size());
Assert.assertEquals(-1, change.ruleBasedSegments.s);
Assert.assertEquals(-1, change.ruleBasedSegments.t);
Assert.assertTrue(fetcher.getLastProxyCheckTimestamp() > 0);

// Set proxy interval to low number to force check for spec 1.3
Field proxyInterval = fetcher.getClass().getDeclaredField("PROXY_CHECK_INTERVAL_MILLISECONDS_SS");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,4 +201,23 @@ public void processTestForException() {

SplitChange splitChange = localhostSplitChangeFetcher.fetch(-1L, -1, fetchOptions);
}

@Test
public void testParseOldSpec() throws FileNotFoundException {
InputStream inputStream = new FileInputStream("src/test/resources/split_old_spec.json");
InputStreamProvider inputStreamProvider = new StaticContentInputStreamProvider(inputStream);
JsonLocalhostSplitChangeFetcher localhostSplitChangeFetcher = new JsonLocalhostSplitChangeFetcher(inputStreamProvider);
FetchOptions fetchOptions = Mockito.mock(FetchOptions.class);

SplitChange splitChange = localhostSplitChangeFetcher.fetch(-1L, -1, fetchOptions);

List<Split> split = splitChange.featureFlags.d;
Assert.assertEquals(7, split.size());
Assert.assertEquals(1660326991072L, splitChange.featureFlags.t);
Assert.assertEquals(-1L, splitChange.featureFlags.s);

Assert.assertEquals(new ArrayList<>(), splitChange.ruleBasedSegments.d);
Assert.assertEquals(-1L, splitChange.ruleBasedSegments.t);
Assert.assertEquals(-1L, splitChange.ruleBasedSegments.s);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,21 @@ public void works() throws IOException, URISyntaxException, InterruptedException
Assert.assertEquals("on_whitelist", client.getTreatment("admin", "push_test"));
client.destroy();
}

@Test
public void testOldSpec() throws IOException, URISyntaxException, InterruptedException, TimeoutException {
SplitClientConfig config = SplitClientConfig.builder()
.splitFile("src/test/resources/split_old_spec.json")
.segmentDirectory("src/test/resources")
.setBlockUntilReadyTimeout(10000)
.build();
SplitFactory splitFactory = SplitFactoryBuilder.build("localhost", config);
SplitClient client = splitFactory.client();
client.blockUntilReady();

Assert.assertEquals("on", client.getTreatment("bilal", "split_1"));
Assert.assertEquals("off", client.getTreatment("bilal", "split_2"));
Assert.assertEquals("v5", client.getTreatment("admin", "split_2"));
client.destroy();
}
}
Loading
Loading